Skip to content

Commit 55f232b

Browse files
authored
feat: TAT-1921 add adl link to perps trade history ADL pill (#21970)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** - Updated ADL fill tag to be clickable and redirect to the MetaMask ADL support article. - Fixed TP/SL tags not appearing <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: updated ADL fill tag to be clickable and redirect to the MetaMask ADL support article CHANGELOG entry: fixed TP/SL tags not appearing ## **Related issues** Fixes: - [TAT-1921: Add link to ADL article when user tap on ADL pill in trade history](https://consensyssoftware.atlassian.net/browse/TAT-1921) - [TAT-1955: Fix missing tp/sl pills in trades activity view regression](https://consensyssoftware.atlassian.net/browse/TAT-1955) ## **Manual testing steps** 1. Update `getUserAddressWithDefault` function to use ADL testing address - (`HyperLiquidWalletService.ts`) ```ts public async getUserAddressWithDefault( accountId?: CaipAccountId, ): Promise<Hex> { return "0x186e806622d9A8209d99694804Ff61bE830bEB2C"; const id = accountId || (await this.getCurrentAccountId()); return this.getUserAddress(id); } ``` 2. Check the Perps Trade history list for the "Auto-Deleveraging" tag 3. Click the tag to be redirected to the ADL support article ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <img width="461" height="88" alt="image" src="https://github.com/user-attachments/assets/c6d388a8-f675-42b9-96c8-6bddfec53dec" /> <img width="461" height="88" alt="image" src="https://github.com/user-attachments/assets/9878b953-36d8-445b-9561-2802efe6d41e" /> https://github.com/user-attachments/assets/9f8120d1-b401-4db2-bcd5-efb90598b719 <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Makes the ADL tag in trades history tappable to open a support article with analytics tracking, and enriches fills from orders to display TP/SL pills; adds supporting constants and tests. > > - **Perps UI**: > - **ADL tag interaction**: Wrap `PerpsTransactionItem` ADL tag in a pressable; opens `PERPS_SUPPORT_ARTICLES_URLS.ADL_URL` via `Linking.openURL` and tracks tap with `usePerpsEventTracking` (`PERPS_ACTIVITY_HISTORY`, `TRADES`, `ADL_LEARN_MORE`, plus `asset` and `order_timestamp`). > - Use `TouchableOpacity` around tag; minor deps added to memo. > - **Hooks**: > - **Transaction history enrichment**: In `usePerpsTransactionHistory`, map fills with matching order `detailedOrderType` before `transformFillsToTransactions` to enable TP/SL pills in trade history. > - **Constants/Events**: > - Add `PERPS_SUPPORT_ARTICLES_URLS.ADL_URL` in `perpsConfig`. > - Extend `eventNames`: add `TAB_NAME`, `ORDER_TIMESTAMP`, `SCREEN_NAME.PERPS_ACTIVITY_HISTORY`, `ACTION_TYPE.ADL_LEARN_MORE`, and `PERPS_HISTORY_TABS`. > - **Tests**: > - Update/add tests for ADL tag press to open URL and emit tracking event. > - Add tests validating fill enrichment for `detailedOrderType` across scenarios. > - Wire `usePerpsEventTracking` in `PerpsTransactionsView` test setup. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit d91cdc8. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent efe74c4 commit 55f232b

File tree

7 files changed

+405
-16
lines changed

7 files changed

+405
-16
lines changed

app/components/UI/Perps/Views/PerpsTransactionsView/PerpsTransactionsView.test.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import React from 'react';
22
import { fireEvent, waitFor, act } from '@testing-library/react-native';
33
import PerpsTransactionsView from './PerpsTransactionsView';
4-
import { usePerpsConnection, usePerpsTransactionHistory } from '../../hooks';
4+
import {
5+
usePerpsConnection,
6+
usePerpsTransactionHistory,
7+
usePerpsEventTracking,
8+
} from '../../hooks';
59
import { backgroundState } from '../../../../../util/test/initial-root-state';
610
import { RootState } from '../../../../../reducers';
711
import renderWithProvider, {
@@ -25,6 +29,7 @@ jest.mock('@react-navigation/native', () => ({
2529
jest.mock('../../hooks', () => ({
2630
usePerpsConnection: jest.fn(),
2731
usePerpsTransactionHistory: jest.fn(),
32+
usePerpsEventTracking: jest.fn(),
2833
}));
2934

3035
// Mock the asset metadata hook to avoid network calls
@@ -109,6 +114,8 @@ describe('PerpsTransactionsView', () => {
109114
usePerpsTransactionHistory as jest.MockedFunction<
110115
typeof usePerpsTransactionHistory
111116
>;
117+
const mockUsePerpsEventTracking =
118+
usePerpsEventTracking as jest.MockedFunction<typeof usePerpsEventTracking>;
112119

113120
beforeEach(() => {
114121
jest.clearAllMocks();
@@ -131,6 +138,10 @@ describe('PerpsTransactionsView', () => {
131138
error: null,
132139
refetch: jest.fn(),
133140
});
141+
142+
mockUsePerpsEventTracking.mockReturnValue({
143+
track: jest.fn(),
144+
});
134145
});
135146

136147
it('should render with filter tabs', () => {

app/components/UI/Perps/components/PerpsTransactionItem/PerpsTransactionItem.test.tsx

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import React from 'react';
22
import { render, fireEvent } from '@testing-library/react-native';
3-
import { StyleSheet } from 'react-native';
3+
import { StyleSheet, Linking } from 'react-native';
44
import PerpsTransactionItem, { FillType } from './PerpsTransactionItem';
55
import { PerpsTransactionSelectorsIDs } from '../../../../../../e2e/selectors/Perps/Perps.selectors';
66
import {
77
PerpsOrderTransactionStatus,
88
PerpsOrderTransactionStatusType,
99
} from '../../types/transactionHistory';
10+
import { MetaMetricsEvents } from '../../../../hooks/useMetrics';
11+
import {
12+
PerpsEventProperties,
13+
PerpsEventValues,
14+
} from '../../constants/eventNames';
15+
import { PERPS_SUPPORT_ARTICLES_URLS } from '../../constants/perpsConfig';
1016

1117
// Mock Redux selector
1218
jest.mock('react-redux', () => ({
@@ -61,6 +67,16 @@ jest.mock('../../../../../../locales/i18n', () => ({
6167
strings: jest.fn((key: string) => key),
6268
}));
6369

70+
// Mock Linking
71+
jest.mock('react-native/Libraries/Linking/Linking', () => ({
72+
openURL: jest.fn(() => Promise.resolve()),
73+
}));
74+
75+
// Mock usePerpsEventTracking
76+
jest.mock('../../hooks', () => ({
77+
usePerpsEventTracking: jest.fn(),
78+
}));
79+
6480
const mockColors = {
6581
black: '#000000',
6682
gray: '#666666',
@@ -144,6 +160,7 @@ const mockTransaction = {
144160
describe('PerpsTransactionItem', () => {
145161
const mockOnPress = jest.fn();
146162
const mockRenderRightContent = jest.fn().mockReturnValue('Right Content');
163+
const mockTrack = jest.fn();
147164

148165
beforeEach(() => {
149166
jest.clearAllMocks();
@@ -156,9 +173,15 @@ describe('PerpsTransactionItem', () => {
156173
useSelector.mockReturnValue(() => ({
157174
address: '0x123',
158175
}));
176+
177+
// Mock usePerpsEventTracking hook
178+
const { usePerpsEventTracking } = jest.requireMock('../../hooks');
179+
usePerpsEventTracking.mockReturnValue({
180+
track: mockTrack,
181+
});
159182
});
160183

161-
it('should render transaction item with correct content', () => {
184+
it('renders transaction item with correct content', () => {
162185
const { getByText, getByTestId } = render(
163186
<PerpsTransactionItem
164187
item={mockTransaction}
@@ -490,6 +513,49 @@ describe('PerpsTransactionItem', () => {
490513
expect(getByTestId('tag-base-info')).toBeTruthy();
491514
});
492515

516+
it('tracks event and opens support URL when ADL tag is pressed', () => {
517+
const adlTransaction = {
518+
...mockTransaction,
519+
asset: 'BTC',
520+
timestamp: 1234567890000,
521+
fill: {
522+
...mockTransaction.fill,
523+
fillType: FillType.AutoDeleveraging,
524+
},
525+
};
526+
527+
const { getByText } = render(
528+
<PerpsTransactionItem
529+
item={adlTransaction}
530+
styles={mockStyles}
531+
onPress={mockOnPress}
532+
renderRightContent={mockRenderRightContent}
533+
/>,
534+
);
535+
536+
const adlTag = getByText('perps.transactions.order.auto_deleveraging');
537+
fireEvent.press(adlTag);
538+
539+
expect(Linking.openURL).toHaveBeenCalledWith(
540+
PERPS_SUPPORT_ARTICLES_URLS.ADL_URL,
541+
);
542+
expect(mockTrack).toHaveBeenCalledWith(
543+
MetaMetricsEvents.PERPS_UI_INTERACTION,
544+
{
545+
[PerpsEventProperties.INTERACTION_TYPE]:
546+
PerpsEventValues.INTERACTION_TYPE.TAP,
547+
[PerpsEventProperties.SCREEN_NAME]:
548+
PerpsEventValues.SCREEN_NAME.PERPS_ACTIVITY_HISTORY,
549+
[PerpsEventProperties.TAB_NAME]:
550+
PerpsEventValues.PERPS_HISTORY_TABS.TRADES,
551+
[PerpsEventProperties.ACTION_TYPE]:
552+
PerpsEventValues.ACTION_TYPE.ADL_LEARN_MORE,
553+
[PerpsEventProperties.ASSET]: 'BTC',
554+
[PerpsEventProperties.ORDER_TIMESTAMP]: 1234567890000,
555+
},
556+
);
557+
});
558+
493559
it('should not display badge for regular fills', () => {
494560
const regularTransaction = {
495561
...mockTransaction,

app/components/UI/Perps/components/PerpsTransactionItem/PerpsTransactionItem.tsx

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import React, { useMemo } from 'react';
2-
import { View, TouchableOpacity, ViewStyle, TextStyle } from 'react-native';
2+
import {
3+
View,
4+
TouchableOpacity,
5+
ViewStyle,
6+
TextStyle,
7+
Linking,
8+
} from 'react-native';
39
import Text, {
410
TextColor,
511
TextVariant,
@@ -16,6 +22,14 @@ import { strings } from '../../../../../../locales/i18n';
1622
import { useSelector } from 'react-redux';
1723
import { selectSelectedInternalAccountByScope } from '../../../../../selectors/multichainAccounts/accounts';
1824
import { EVM_SCOPE } from '../../../Earn/constants/networks';
25+
import { noop } from 'lodash';
26+
import { PERPS_SUPPORT_ARTICLES_URLS } from '../../constants/perpsConfig';
27+
import { usePerpsEventTracking } from '../../hooks';
28+
import { MetaMetricsEvents } from '../../../../hooks/useMetrics';
29+
import {
30+
PerpsEventProperties,
31+
PerpsEventValues,
32+
} from '../../constants/eventNames';
1933

2034
export enum FillType {
2135
Standard = 'standard',
@@ -53,6 +67,8 @@ const PerpsTransactionItem: React.FC<PerpsTransactionItemProps> = ({
5367
EVM_SCOPE,
5468
);
5569

70+
const { track } = usePerpsEventTracking();
71+
5672
const fillTag = useMemo(() => {
5773
const { fill } = item;
5874

@@ -101,18 +117,42 @@ const PerpsTransactionItem: React.FC<PerpsTransactionItemProps> = ({
101117
return null;
102118
}
103119

120+
let onTagPress = noop;
121+
122+
if (fill.fillType === FillType.AutoDeleveraging) {
123+
onTagPress = () => {
124+
Linking.openURL(PERPS_SUPPORT_ARTICLES_URLS.ADL_URL).catch((error) => {
125+
console.error('Error opening ADL support article:', error);
126+
});
127+
track(MetaMetricsEvents.PERPS_UI_INTERACTION, {
128+
[PerpsEventProperties.INTERACTION_TYPE]:
129+
PerpsEventValues.INTERACTION_TYPE.TAP,
130+
[PerpsEventProperties.SCREEN_NAME]:
131+
PerpsEventValues.SCREEN_NAME.PERPS_ACTIVITY_HISTORY,
132+
[PerpsEventProperties.TAB_NAME]:
133+
PerpsEventValues.PERPS_HISTORY_TABS.TRADES,
134+
[PerpsEventProperties.ACTION_TYPE]:
135+
PerpsEventValues.ACTION_TYPE.ADL_LEARN_MORE,
136+
[PerpsEventProperties.ASSET]: item.asset,
137+
[PerpsEventProperties.ORDER_TIMESTAMP]: item.timestamp,
138+
});
139+
};
140+
}
141+
104142
return (
105-
<TagBase
106-
shape={TagShape.Pill}
107-
severity={tagConfig.severity}
108-
includesBorder={tagConfig.includesBorder}
109-
>
110-
<Text variant={TextVariant.BodyXSMedium} color={tagConfig.textColor}>
111-
{tagConfig.label}
112-
</Text>
113-
</TagBase>
143+
<TouchableOpacity onPress={onTagPress}>
144+
<TagBase
145+
shape={TagShape.Pill}
146+
severity={tagConfig.severity}
147+
includesBorder={tagConfig.includesBorder}
148+
>
149+
<Text variant={TextVariant.BodyXSMedium} color={tagConfig.textColor}>
150+
{tagConfig.label}
151+
</Text>
152+
</TagBase>
153+
</TouchableOpacity>
114154
);
115-
}, [item, selectedAccount?.address]);
155+
}, [item, selectedAccount?.address, track]);
116156

117157
return (
118158
<TouchableOpacity

app/components/UI/Perps/constants/eventNames.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ export const PerpsEventProperties = {
1212
ASSET: 'asset',
1313
DIRECTION: 'direction',
1414
SOURCE: 'source',
15+
TAB_NAME: 'tab_name',
1516

1617
// Trade properties
1718
LEVERAGE: 'leverage',
1819
LEVERAGE_USED: 'leverage_used',
1920
ORDER_SIZE: 'order_size',
2021
MARGIN_USED: 'margin_used',
2122
ORDER_TYPE: 'order_type', // lowercase per dashboard
23+
ORDER_TIMESTAMP: 'order_timestamp',
2224
LIMIT_PRICE: 'limit_price',
2325
FEES: 'fees',
2426
FEE: 'fee',
@@ -169,6 +171,7 @@ export const PerpsEventValues = {
169171
SKIP: 'skip',
170172
STOP_LOSS_SET: 'stop_loss_set',
171173
TAKE_PROFIT_SET: 'take_profit_set',
174+
ADL_LEARN_MORE: 'adl_learn_more',
172175
},
173176
NOTIFICATION_TYPE: {
174177
POSITION_LIQUIDATED: 'position_liquidated',
@@ -215,8 +218,15 @@ export const PerpsEventValues = {
215218
},
216219
SCREEN_NAME: {
217220
CONNECTION_ERROR: 'connection_error',
221+
PERPS_ACTIVITY_HISTORY: 'perps_activity_history',
218222
},
219223
ACTION: {
220224
CONNECTION_RETRY: 'connection_retry',
221225
},
226+
PERPS_HISTORY_TABS: {
227+
TRADES: 'trades',
228+
ORDERS: 'orders',
229+
FUNDING: 'funding',
230+
DEPOSITS: 'deposits',
231+
},
222232
} as const;

app/components/UI/Perps/constants/perpsConfig.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,3 +411,12 @@ export const SUPPORT_CONFIG = {
411411
TITLE_KEY: 'perps.support.title',
412412
DESCRIPTION_KEY: 'perps.support.description',
413413
} as const;
414+
415+
/**
416+
* Support article URLs
417+
* Links to specific MetaMask support articles for Perps features
418+
*/
419+
export const PERPS_SUPPORT_ARTICLES_URLS = {
420+
ADL_URL:
421+
'https://support.metamask.io/manage-crypto/trade/perps/leverage-and-liquidation/#what-is-auto-deleveraging-adl',
422+
} as const;

0 commit comments

Comments
 (0)