Skip to content

feat(perps): implement Pro mode market detail header - #33899

Merged
michalconsensys merged 5 commits into
mainfrom
feat/metamask-mobile-perps-pro-market-header
Jul 30, 2026
Merged

feat(perps): implement Pro mode market detail header#33899
michalconsensys merged 5 commits into
mainfrom
feat/metamask-mobile-perps-pro-market-header

Conversation

@michalconsensys

@michalconsensys michalconsensys commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Description

Implements the Perps Pro-mode market detail header from Figma, replacing the previous scaffold (symbol + placeholder box).

Motivation: Pro market details already routes to PerpsProMarketView, but the fixed header was a placeholder. This brings it in line with the design and with the Lite-mode market-detail patterns (back, identity → market list, watchlist, mode pill).

Solution:

  • Rework PerpsProMarketHeader into the full layout: back button; tappable asset identity (token logo, name, leverage tag, market-list caret, and [Ticker]-USD perp subtitle); wallet, favorite star, and read-only Lite/Pro mode pill (PerpsModeToggle variant="active").
  • Use a content-hugging identity layout with an explicit title max-width (matching Figma) so the asset name, leverage, and ticker render reliably and long names truncate cleanly.
  • Wire handlers in PerpsProMarketView (back with home fallback, market list + analytics, watchlist toggle with limit guard, wallet → Perps home, mode switch + flash).
  • Add header test IDs and unit coverage for the controls and subtitle.

Scope note: live price (below the header in PerpsProMarketSummary) and PnL (in PerpsProPositionsPanel) are unchanged — the Figma header node does not include them.

Changelog

CHANGELOG entry: Added the Pro mode market detail header with asset identity, watchlist, and Lite/Pro mode controls

Related issues

Fixes: N/A

Manual testing steps

Feature: Perps Pro market detail header

  Scenario: user opens a market in Pro mode and sees the redesigned header
    Given Perps Pro mode is enabled and the user is on a market detail screen
    When the screen loads
    Then the header shows a back button, token icon, asset name, leverage tag, market-list caret, and ticker-USD perp subtitle
    And the header shows wallet, favorite, and Pro mode pill controls

  Scenario: user opens the market list from the header identity
    Given the user is on a Pro market detail screen
    When the user taps the asset identity in the header
    Then the market list opens

  Scenario: user toggles watchlist from the header
    Given the user is on a Pro market detail screen
    When the user taps the favorite star
    Then the market is added to or removed from the watchlist

  Scenario: user switches to Lite mode from the header pill
    Given the user is on a Pro market detail screen
    When the user taps the Pro mode pill
    Then the app switches to Lite mode and shows the mode flash

Screenshots/Recordings

N/A — UI change implemented from Figma; screenshots to be attached after device/simulator verification.

Before

Scaffold header (symbol + muted placeholder box).

After

Figma Pro market header: back · token · name / leverage / caret · subtitle · wallet · star · Pro pill.

Pre-merge author checklist

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 to import wallets with many accounts and tokens
  • I've instrumented key operations with Sentry traces for production performance metrics

For performance guidelines and tooling, see the Performance Guide.

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.

Note

Low Risk
UI and navigation/watchlist wiring in Perps with existing patterns; no auth or payment changes. Broad test coverage limits regression risk.

Overview
Replaces the Pro market detail scaffold header (symbol text + placeholder) with the full Figma layout: back, tappable asset identity (logo, name, leverage, market-list caret, Ticker-USD perp subtitle), wallet, watchlist star, and Lite/Pro mode pill.

Introduces shared PerpsMarketIdentity (also wired into Lite PerpsMarketInlineHeader detail layout) and usePerpsProMarketHeaderActions to centralize navigation, watchlist toggle, analytics on market-list tap, mode switch + flash, and back-with-home-fallback. Adds header test IDs, i18n strings for a11y labels, and unit tests for the header, identity component, hook, and screen integration.

Reviewed by Cursor Bugbot for commit 73b0bad. Bugbot is set up for automated code reviews on this repo. Configure here.

Replace the placeholder Pro market header scaffold with the full Figma
design: back button, tappable asset identity (token logo, name, leverage
tag, market-list caret, and ticker-USD perp subtitle), plus wallet,
watchlist favorite, and the read-only Lite/Pro mode pill.

Wire the header handlers in PerpsProMarketView (back, market list,
watchlist toggle, wallet navigation, and mode switch) and add the
corresponding header test IDs and coverage.
@github-actions

Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamask-ci metamask-ci Bot added the team-perps Perps team label Jul 28, 2026
@metamask-ci

metamask-ci Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

PR template — items to address before "Ready for review"

Warnings — informational, address before merging:

See docs/readme/ready-for-review.md for the full Definition of Ready for Review.

Replace the collapsing nested flex layout in the identity column with a
content-hugging layout and an explicit max-width title cap (matching Figma),
so the asset name, leverage tag, and ticker subtitle render reliably.
@michalconsensys
michalconsensys marked this pull request as ready for review July 28, 2026 15:14
@michalconsensys
michalconsensys requested a review from a team as a code owner July 28, 2026 15:14

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 615f90d. Configure here.

@github-actions github-actions Bot added the risk:low AI analysis: low risk label Jul 28, 2026
Narrow the market prop to Partial with a required symbol so lint:tsc
accepts route params after the symbol guard, and add dedicated header
unit tests plus view handler coverage for the new Pro header actions.
@github-actions github-actions Bot added size-L and removed size-M labels Jul 28, 2026
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

🧪 Flaky unit test detection

✅ All previously detected unit test flakiness issues in this PR have been fixed.

View recent run history

This check is informational only and does not block merging.

Extract header back/market-list/wallet/watchlist/mode handlers into
usePerpsProMarketHeaderActions with full unit coverage so new-code
coverage clears Sonar's 80% gate (View handler bodies were not credited
in the merged CI lcov).
@github-actions

Copy link
Copy Markdown
Contributor

⚡ Performance Test Results

ℹ️ Performance test results are currently non-blocking and will not block this PR.

All tests passed · 2 tests · 1 device

📱 Devices tested (1)

Android: Google Pixel 8 Pro (v14.0)

✅ Passed Tests (2)
Test Platform Device Duration Team Recording
Perps add funds Android Google Pixel 8 Pro (v14.0) 8.63s @mm-perps-engineering-team 📹 Watch
Perps open position and close it Android Google Pixel 8 Pro (v14.0) 16.07s @mm-perps-engineering-team 📹 Watch

Branch: feat/metamask-mobile-perps-pro-market-header · Build: Normal · Commit: 24e3df5 · View full run

@geositta geositta left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good and the header actions are behind a hook - that extraction is the right call and I would like to see the Lite view adopt it too.

Two things I would ideally like to fix in this PR: the duplicated identity block (comment 1) and the missing accessibility labels (comment 2). Both are small and both get harder to retrofit once other suites and screens key off this markup.

The rest are refinements you can fold in here or track for the next Pro ticket; none of them should hold up UAT.

collateral: PERPS_COLLATERAL_SYMBOL,
});

const renderIdentity = (pressed: boolean) => (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This identity block is a near-verbatim copy of PerpsMarketInlineHeader.tsx:97 Same structure, same perps.market_details.perp_pair subtitle, same ArrowRight caret, same rounded-lg p-1 ${pressed ? 'bg-pressed' : ''}, same Pressable + accessibilityLabel={strings('perps.market_details.market_list')} wrapper - only the token size (32 vs 40), gap (2 vs 3), and testID constants differ.

Two copies of the same Figma spec will drift the first time design tweaks the identity row. Would prefer we extract a PerpsMarketIdentity component taking { symbol, name, maxLeverage, size, onPress, testIDs } and have both headers render it. This is the one change I would most like to see land with this PR.

gap={2}
>
{onBackPress ? (
<ButtonIcon

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ButtonIcon sets accessible but derives no label from iconName, so back,
wallet, and favorite each announce as an unlabeled button to VoiceOver/TalkBack. ButtonIconProps extends PressableProps, so accessibilityLabel is typed and passes through to the underlying Pressable — PerpsChartFullscreenModal.tsx:255 already does this.

Add a strings()-sourced label to each, and give the star a state-aware one so the toggle is discoverable without seeing the fill:

<ButtonIcon
  iconName={isFavorite ? IconName.StarFilled : IconName.Star}
  size={ButtonIconSize.Md}
  onPress={onFavoritePress}
  accessibilityLabel={strings(
    isFavorite
      ? 'perps.market_details.remove_from_watchlist'
      : 'perps.market_details.add_to_watchlist',
  )}
testID={PerpsProMarketViewSelectorsIDs.HEADER_FAVORITE_BUTTON}
/>

Those two locale keys need adding - locales/languages/en.json under perps.market_details (which already holds perp_pair and market_list).

*/
market: Partial<PerpsMarketData> & { symbol: string };
/** Active Perps mode — drives the read-only mode pill on the right. */
mode: PerpsMode;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PerpsProMarketHeader.tsx:38 and :191
mode is required, but only rendered when onModeChange is supplied

Callers that omit onModeChange must still pass a mode that is never displayed. Either make mode optional alongside the handler, or accept the mode pill as a modeToggle?: ReactNode slot the way PerpsMarketInlineHeader takes endAccessory.

}, [symbol, isWatchlist, addToWatchlist, removeFromWatchlist]);

const handlePerpsModeChange = useCallback(
(nextMode: PerpsMode) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the hook duplicates logic already in the Lite view handlePerpsModeChange matches PerpsMarketDetailsView.tsx:346-352 line for line, handleMarketListPress matches :875-891, and handleBackPress matches :827-833.

Pulling these into a hook is the right instinct, prefer we finish the job by having PerpsMarketDetailsView consume the same hook, which deletes ~50 lines there and guarantees the two screens track the same analytics
payloads.

}

if (isWatchlist) {
removeFromWatchlist(symbol);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unawaited promises from the watchlist actions addToWatchlist /removeFromWatchlist are async.

They swallow their own errors today, so nothing breaks, but the call site relies on that undocumented guarantee. Mark it deliberate (void addToWatchlist(symbol); with a one-line comment on the fire-and-forget/optimistic-update contract) so a future change to those functions surfaces here.

Extract PerpsMarketIdentity for Lite/Pro headers, add VoiceOver labels, make mode optional, and mark watchlist updates as fire-and-forget.
@github-actions github-actions Bot added size-XL and removed size-L labels Jul 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokeAccounts, SmokeConfirmations, SmokeNetworkAbstractions, SmokeNetworkExpansion, SmokeSwap, SmokeStake, SmokeWalletPlatform, SmokeMoney, SmokePerps, SmokeMultiChainAPI, SmokePredictions, SmokeSeedlessOnboarding, SmokeBrowser, SmokeSnaps, SmokeMMConnect
  • Selected Performance tags: None (no tests recommended)
  • Risk Level: high
  • AI Confidence: 100%
click to see 🤖 AI reasoning details

E2E Test Selection:
Hard rule (en-locale-change): locales/languages/en.json changed — UI strings and E2E text/label selectors may diverge (including platform casing like Android textAllCaps). Running all tests.

Performance Test Selection:
No performance-sensitive changes in this PR. The changes are UI component refactoring (extracting PerpsMarketIdentity as a shared component), adding header action handlers, and new localization strings. There are no changes to data fetching, rendering loops, controller state management, or other performance-critical paths. The @PerformancePreps tag is not warranted as the changes are purely UI/header-level and don't affect the perps market loading, position management, or add funds flow performance.

View GitHub Actions results

@github-actions github-actions Bot added risk:medium AI analysis: medium risk and removed risk:low AI analysis: low risk labels Jul 30, 2026
@sonarqubecloud

Copy link
Copy Markdown

@abretonc7s abretonc7s left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with comments

/>
</Box>

<Box flexDirection={BoxFlexDirection.Row} alignItems={BoxAlignItems.Center}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] The trailing action row (wallet, favorite, mode pill) has no horizontal gap. Lite’s Pro-enabled endAccessory and design-system HeaderSubpage multi-icon layout both use gap={2} / gap-2. With ButtonIconSize.Md at h-8 w-8, the controls sit flush and will look denser than the Lite header and Figma spacing.

Suggestion: Add gap={2} (or equivalent twClassName="gap-2") on the end-actions Box, matching PerpsMarketDetailsView’s end accessory and HeaderSubpage.

>
<PerpsProMarketHeader symbol={symbol} />
<PerpsProMarketHeader
market={{ ...market, symbol: market.symbol }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] The header is fed only route.params.market. Lite market details explicitly enrich partial route markets (e.g. Recent Activity / deep links missing formatted maxLeverage) via usePerpsMarkets before rendering identity. On Pro, those entries will omit the leverage tag and fall back to the ticker for the title, so the new header can look incomplete on common entry paths even though the screen still loads.

Suggestion: Mirror Lite’s enrichment (needsEnrichment + markets lookup, or shared helper) before passing market into PerpsProMarketHeader, so name/leverage stay populated when route params are minimal.

).toHaveTextContent('BTC-USD perp');
});

it('wires header actions from usePerpsProMarketHeaderActions', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] The integration test “wires header actions from usePerpsProMarketHeaderActions” presses back / market list / wallet / favorite, but never the mode pill, even though mockHandlePerpsModeChange is already mocked. Mode switch is a primary header action (and the path that remounts Lite via PerpsMarketDetailsRouter).

Suggestion: Press PerpsModeToggleSelectorsIDs.PRO_SEGMENT (or the rendered active segment) and assert mockHandlePerpsModeChange was called once (with PerpsMode.Lite if the real toggle is used, or just called if the toggle is stubbed).

* actions remain placeholders until their owning capability is implemented.
* Left: back button. Center: tappable asset identity (token icon, name,
* leverage pill, market-list caret, and `[Ticker]-[collateral] perp`
* subtitle). Right: wallet, favorite (watchlist star), and the read-only

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Comments/JSDoc still describe the mode control as a “read-only” Lite/Pro pill. PerpsModeToggle variant="active" is interactive and flips mode (same as Lite market details).

Suggestion: Drop “read-only” and describe it as the active-mode pill that switches Lite/Pro.

@michalconsensys
michalconsensys added this pull request to the merge queue Jul 30, 2026
Merged via the queue into main with commit adc9c97 Jul 30, 2026
136 of 138 checks passed
@michalconsensys
michalconsensys deleted the feat/metamask-mobile-perps-pro-market-header branch July 30, 2026 12:42
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 30, 2026
@metamask-ci metamask-ci Bot added the release-8.6.0 Issue or pull request that will be included in release 8.6.0 label Jul 30, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-8.6.0 Issue or pull request that will be included in release 8.6.0 risk:medium AI analysis: medium risk size-XL team-perps Perps team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants