Skip to content

Commit c400fda

Browse files
authored
fix: Polish Perps filter patterns on Market and Transactions screens (MetaMask#29391)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** This PR polishes the filter patterns on the Market and Transactions pages for consistency. It is part of a wider initiative to standardize our design patterns. [See this file ](https://www.figma.com/design/aRyo0N82L0IoNlMVIKvSpc/Mobile-Papercuts?node-id=1204-1329)for context. ## **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: null ## **Related issues** Fixes: MetaMask#28711 MetaMask#29396 ## **Manual testing steps** ```gherkin Feature: Perps market category filters (design system badge, no dismiss icon) Scenario: user selects and clears a perps market category using chip taps only Given I am on the Perps markets list and all category chips are visible with none selected (or a known starting filter) When user taps a category chip to apply that filter, then taps that same chip again Then the list filters when the chip is selected, no dismiss “×” appears on the chip, and tapping the selected chip again clears the filter back to all markets ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <img width="768" height="771" alt="Screenshot 2026-04-28 at 8 54 33 PM" src="https://github.com/user-attachments/assets/43fb350b-68ae-438a-b6cc-f771ec0beee8" /> ### **After** <img width="1285" height="743" alt="Screenshot 2026-04-28 at 1 43 59 PM" src="https://github.com/user-attachments/assets/bded9482-933e-4214-8b3f-78fd6420ef74" /> ## **Pre-merge author checklist** <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [ ] 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). - [ ] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] 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. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] 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] > **Low Risk** > Low risk UI polish and minor component API simplification; main risk is unintended visual/regression in Perps category filtering due to removing the badge dismiss affordance. > > **Overview** > Polishes Perps filter UI spacing to be consistent across screens (e.g., tighter gaps between Transactions filter tabs and adjusted Market search bar padding). > > Simplifies `PerpsMarketCategoryBadge` by removing the `showDismiss`/`onDismiss` behavior and dismiss icon, updating styling/typography, and adjusting `PerpsMarketCategoryBadges` plus tests to reflect the new toggle-off-by-retap interaction without dismiss indicators. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 5e65e5f. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 3b93894 commit c400fda

8 files changed

Lines changed: 22 additions & 101 deletions

File tree

app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.styles.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ const styleSheet = (params: { theme: Theme }) => {
137137
},
138138
searchBarRow: {
139139
paddingHorizontal: 16,
140-
paddingTop: 12,
141140
paddingBottom: 8,
142141
},
143142
searchContainer: {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ const PerpsTransactionsView: React.FC = () => {
477477
<ScrollView
478478
horizontal
479479
showsHorizontalScrollIndicator={false}
480-
contentContainerStyle={tw.style('flex-row gap-3')}
480+
contentContainerStyle={tw.style('flex-row gap-2')}
481481
pointerEvents="auto"
482482
scrollEnabled={false}
483483
>
@@ -501,7 +501,7 @@ const PerpsTransactionsView: React.FC = () => {
501501
<View style={styles.filterContainer} pointerEvents="box-none">
502502
<ScrollView
503503
horizontal
504-
contentContainerStyle={tw.style('flex-row gap-3')}
504+
contentContainerStyle={tw.style('flex-row gap-2')}
505505
showsHorizontalScrollIndicator={false}
506506
pointerEvents="auto"
507507
scrollEnabled

app/components/UI/Perps/components/PerpsMarketCategoryBadge/PerpsMarketCategoryBadge.styles.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,10 @@ export const styleSheet = (params: {
2020
justifyContent: 'center',
2121
paddingHorizontal: 12,
2222
paddingVertical: 8,
23-
borderRadius: 8,
23+
borderRadius: 12,
2424
backgroundColor: isSelected
2525
? theme.colors.icon.default
2626
: theme.colors.background.muted,
27-
gap: 4,
2827
},
2928
badgePressed: {
3029
opacity: 0.7,

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

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe('PerpsMarketCategoryBadge', () => {
2121
expect(getByText('Crypto')).toBeTruthy();
2222
});
2323

24-
it('calls onPress when pressed and not showing dismiss', () => {
24+
it('calls onPress when pressed', () => {
2525
const onPress = jest.fn();
2626
const { getByText } = render(
2727
<PerpsMarketCategoryBadge {...defaultProps} onPress={onPress} />,
@@ -31,50 +31,6 @@ describe('PerpsMarketCategoryBadge', () => {
3131
expect(onPress).toHaveBeenCalledTimes(1);
3232
});
3333

34-
it('does not show dismiss icon when showDismiss is false', () => {
35-
const { queryByTestId } = render(
36-
<PerpsMarketCategoryBadge
37-
{...defaultProps}
38-
testID="badge"
39-
showDismiss={false}
40-
/>,
41-
);
42-
43-
expect(queryByTestId('badge-dismiss')).toBeNull();
44-
});
45-
46-
it('shows dismiss icon when showDismiss is true', () => {
47-
const { getByTestId } = render(
48-
<PerpsMarketCategoryBadge
49-
{...defaultProps}
50-
testID="badge"
51-
showDismiss
52-
isSelected
53-
/>,
54-
);
55-
56-
expect(getByTestId('badge-dismiss')).toBeTruthy();
57-
});
58-
59-
it('calls onDismiss when pressed with showDismiss true', () => {
60-
const onDismiss = jest.fn();
61-
const onPress = jest.fn();
62-
const { getByTestId } = render(
63-
<PerpsMarketCategoryBadge
64-
{...defaultProps}
65-
testID="badge"
66-
showDismiss
67-
isSelected
68-
onPress={onPress}
69-
onDismiss={onDismiss}
70-
/>,
71-
);
72-
73-
fireEvent.press(getByTestId('badge'));
74-
expect(onDismiss).toHaveBeenCalledTimes(1);
75-
expect(onPress).not.toHaveBeenCalled();
76-
});
77-
7834
it('has correct accessibility properties', () => {
7935
const { getByRole } = render(
8036
<PerpsMarketCategoryBadge {...defaultProps} isSelected />,

app/components/UI/Perps/components/PerpsMarketCategoryBadge/PerpsMarketCategoryBadge.tsx

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
import React, { useCallback } from 'react';
1+
import React from 'react';
22
import { Pressable } from 'react-native';
3-
import Text, {
3+
import {
4+
FontWeight,
5+
Text,
46
TextVariant,
5-
} from '../../../../../component-library/components/Texts/Text';
6-
import Icon, {
7-
IconName,
8-
IconSize,
9-
IconColor,
10-
} from '../../../../../component-library/components/Icons/Icon';
7+
} from '@metamask/design-system-react-native';
118
import { useStyles } from '../../../../../component-library/hooks';
129
import { styleSheet } from './PerpsMarketCategoryBadge.styles';
1310
import type { PerpsMarketCategoryBadgeProps } from './PerpsMarketCategoryBadge.types';
@@ -16,59 +13,40 @@ import type { PerpsMarketCategoryBadgeProps } from './PerpsMarketCategoryBadge.t
1613
* PerpsMarketCategoryBadge - Interactive badge for category filtering
1714
*
1815
* Displays a pressable badge/pill for selecting market categories.
19-
* When selected and showDismiss is true, shows an "×" icon to clear the filter.
2016
*
2117
* @example
2218
* ```tsx
2319
* <PerpsMarketCategoryBadge
24-
* category="crypto"
2520
* label="Crypto"
2621
* isSelected={selectedCategory === 'crypto'}
27-
* showDismiss={selectedCategory === 'crypto'}
28-
* onPress={() => setSelectedCategory('crypto')}
29-
* onDismiss={() => setSelectedCategory('all')}
22+
* onPress={() => handleCategoryPress('crypto')}
3023
* />
3124
* ```
3225
*/
3326
const PerpsMarketCategoryBadge: React.FC<PerpsMarketCategoryBadgeProps> = ({
3427
label,
3528
isSelected,
36-
showDismiss = false,
3729
onPress,
38-
onDismiss,
3930
testID,
4031
}) => {
4132
const { styles } = useStyles(styleSheet, { isSelected });
4233

43-
const handlePress = useCallback(() => {
44-
if (showDismiss && onDismiss) {
45-
// If showing dismiss and clicked, treat as dismiss
46-
onDismiss();
47-
} else {
48-
onPress();
49-
}
50-
}, [showDismiss, onDismiss, onPress]);
51-
5234
return (
5335
<Pressable
5436
style={({ pressed }) => [styles.badge, pressed && styles.badgePressed]}
55-
onPress={handlePress}
37+
onPress={onPress}
5638
testID={testID}
5739
accessibilityRole="button"
5840
accessibilityState={{ selected: isSelected }}
5941
accessibilityLabel={label}
6042
>
61-
<Text variant={TextVariant.BodySM} style={styles.badgeText}>
43+
<Text
44+
variant={TextVariant.BodyMd}
45+
fontWeight={FontWeight.Medium}
46+
style={styles.badgeText}
47+
>
6248
{label}
6349
</Text>
64-
{showDismiss && (
65-
<Icon
66-
name={IconName.Close}
67-
size={IconSize.Xs}
68-
color={IconColor.Inverse}
69-
testID={testID ? `${testID}-dismiss` : undefined}
70-
/>
71-
)}
7250
</Pressable>
7351
);
7452
};

app/components/UI/Perps/components/PerpsMarketCategoryBadge/PerpsMarketCategoryBadge.types.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,10 @@ export interface PerpsMarketCategoryBadgeProps {
77
* Whether this badge is currently selected
88
*/
99
isSelected: boolean;
10-
/**
11-
* Whether to show the dismiss "×" icon (typically when selected)
12-
*/
13-
showDismiss?: boolean;
1410
/**
1511
* Callback when the badge is pressed
1612
*/
1713
onPress: () => void;
18-
/**
19-
* Callback when the dismiss icon is pressed (only used when showDismiss is true)
20-
*/
21-
onDismiss?: () => void;
2214
/**
2315
* Optional test ID for E2E testing
2416
*/

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ describe('PerpsMarketCategoryBadges', () => {
7070
});
7171

7272
describe('Selected state (category selected)', () => {
73-
it('shows all badges when a category is selected, with dismiss icon on selected one', () => {
74-
const { getByText, getByTestId, queryByTestId } = render(
73+
it('shows all badges when a category is selected, without dismiss icons', () => {
74+
const { getByText, queryByTestId } = render(
7575
<PerpsMarketCategoryBadges
7676
{...defaultProps}
7777
selectedCategory="crypto"
@@ -84,8 +84,7 @@ describe('PerpsMarketCategoryBadges', () => {
8484
expect(getByText('Commodities')).toBeTruthy();
8585
expect(getByText('Forex')).toBeTruthy();
8686

87-
// Selected badge should show dismiss icon, others should not
88-
expect(getByTestId('category-badges-crypto-dismiss')).toBeTruthy();
87+
expect(queryByTestId('category-badges-crypto-dismiss')).toBeNull();
8988
expect(queryByTestId('category-badges-stocks-dismiss')).toBeNull();
9089
expect(queryByTestId('category-badges-commodities-dismiss')).toBeNull();
9190
expect(queryByTestId('category-badges-forex-dismiss')).toBeNull();
@@ -154,12 +153,12 @@ describe('PerpsMarketCategoryBadges', () => {
154153
);
155154

156155
// Should show available categories (fallback to "all" view)
157-
// No auto-reset - user must explicitly dismiss via badge click
156+
// No auto-reset user must tap a badge to change selection
158157
expect(getByText('Crypto')).toBeTruthy();
159158
expect(getByText('Stocks')).toBeTruthy();
160159
// Forex is not in availableCategories, so it shouldn't show
161160
expect(queryByText('Forex')).toBeNull();
162-
// No auto-reset - filter is only reset when user clicks dismiss on a selected badge
161+
// No auto-reset filter clears when user taps the selected category again
163162
expect(onCategorySelect).not.toHaveBeenCalled();
164163
});
165164

app/components/UI/Perps/components/PerpsMarketCategoryBadges/PerpsMarketCategoryBadges.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const DEFAULT_CATEGORIES: CategoryBadgeConfig[] = [
3030
*
3131
* Always displays all category badges in a horizontal scroll.
3232
* The selected category is visually highlighted.
33-
* Tapping a selected badge deselects it (toggles back to 'all').
33+
* Tapping a selected badge again deselects it (toggles back to 'all').
3434
*
3535
* @example
3636
* ```tsx
@@ -93,9 +93,7 @@ const PerpsMarketCategoryBadges: React.FC<PerpsMarketCategoryBadgesProps> = ({
9393
<PerpsMarketCategoryBadge
9494
label={strings(config.labelKey)}
9595
isSelected={isCategorySelected}
96-
showDismiss={isCategorySelected}
9796
onPress={() => handleCategoryPress(config.category)}
98-
onDismiss={() => onCategorySelect('all')}
9997
testID={testID ? `${testID}-${config.category}` : undefined}
10098
/>
10199
</Animated.View>

0 commit comments

Comments
 (0)