Skip to content

feat(perps): redesign market detail header (TAT-3348)#33024

Merged
aganglada merged 12 commits into
mainfrom
feat/perps-change-market-headers-TAT-3348
Jul 15, 2026
Merged

feat(perps): redesign market detail header (TAT-3348)#33024
aganglada merged 12 commits into
mainfrom
feat/perps-change-market-headers-TAT-3348

Conversation

@michalconsensys

@michalconsensys michalconsensys commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Description

Redesigns the Perps market detail header to match the new design in TAT-3348, taking inspiration from the equivalent extension implementation.

What changed

  • The header now shows a two-row market identity:
    • First row: the full asset name (e.g. Ethereum), the leverage pill (e.g. 50x), and a chevron that opens the market list.
    • Second row: the [Ticker]-[collateral] perp pair subtitle (e.g. ETH-USD perp).
  • The back chevron stays on the left and the favorite star on the right.
  • The live price + 24h price change and the fullscreen chart button move into a dedicated row below the header (the price is shown more prominently).

Why

The previous header showed ETH-USD as the title with the price inline and expand/star/search icons on the right. The new layout aligns mobile with the agreed design and the extension, surfacing the full asset name and market pair, and grouping the price/chart controls below the header.

Implementation notes

  • PerpsMarketInlineHeader gains an opt-in showAssetName prop (plus onMarketListPress for the chevron). The default layout is unchanged, so the shared order book header is not affected.
  • LivePriceHeader gains a size variant (large) used for the prominent below-header price; the default size is unchanged.
  • The collateral label uses USD to stay consistent with the app's existing -USD convention.
  • The fullscreen chart button is not present in the extension yet, so mobile's existing fullscreen behavior is preserved and only relocated per the acceptance criteria.

Changelog

CHANGELOG entry: Redesigned the Perps market detail header to show the full asset name, leverage, and market pair, with the live price, 24h change, and fullscreen chart button below the header.

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/TAT-3348

Manual testing steps

Feature: Perps market detail header

  Scenario: user opens a perps market detail screen
    Given the user is on the Perps market list
    When the user taps a market (e.g. ETH)
    Then the header first row shows the full asset name, the leverage pill, and a chevron
    And the header second row shows the market pair as "ETH-USD perp"
    And the back chevron is shown on the left and the favorite star on the right
    And the live price and 24h price change are shown below the header
    And a fullscreen chart button is shown below the header

  Scenario: user opens the market list from the header chevron
    Given the user is on a perps market detail screen
    When the user taps the chevron next to the asset name
    Then the market list is opened

  Scenario: user opens the fullscreen chart
    Given the user is on a perps market detail screen
    When the user taps the fullscreen chart button below the header
    Then the fullscreen chart modal is displayed

Screenshots/Recordings

Before

After

simulator_screenshot_A02336D0-CDD5-4B3D-BA8E-456DA2EC0C70

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-only Perps market details layout and analytics property updates; default inline header paths are preserved and coverage is extensive in unit/view tests.

Overview
Redesigns the Perps market detail screen header and price row to match TAT-3348 / extension-style layout.

Header (useDetailLayout on PerpsMarketInlineHeader): Title becomes the full asset name with leverage pill and a market-list chevron (replacing header fullscreen + category search). Subtitle is the localized {{ticker}}-{{collateral}} perp pair via new PERPS_COLLATERAL_SYMBOL; live price is no longer in the header. Compact header behavior elsewhere is unchanged.

Below header: New market summary row with LivePriceHeader size="large" (prominent price, stacked layout, absolute + % 24h change) and a relocated fullscreen chart button. LivePriceHeader migrates to design-system Text tokens and adds shrink/ellipsis for long prices.

Analytics & i18n: Market list entry reports button_clicked: market_list, button_location: perp_market_details, source: perp_asset_screen; metrics docs note legacy magnifying-glass values. New strings for market list, perp pair, and updated test IDs.

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

Update the Perps market detail header to the new design:
- First row: full asset name, leverage pill, and a chevron that opens the
  market list
- Second row: [Ticker]-[collateral] perp pair subtitle
- Back chevron on the left, favorite star on the right
- Move the live price + 24h change and the fullscreen chart button into a
  dedicated row below the header

The shared PerpsMarketInlineHeader keeps its previous layout by default
(showAssetName opt-in), so the order book header is unchanged. Adds a
`size` variant to LivePriceHeader for the larger below-header price.
@github-actions

github-actions Bot commented Jul 9, 2026

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 9, 2026
@metamask-ci

metamask-ci Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

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

Warnings — informational, address before merging:

  • Pre-merge author checklist has unchecked items (e.g. "I've tested on Android"). Every box must be consciously checked — see docs/readme/ready-for-review.md.

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

- Track the market-list arrow with BUTTON_CLICKED.MARKET_LIST and
  SOURCE.PERP_ASSET_SCREEN instead of the stale magnifying-glass values;
  rename handler to handleMarketListPress
- Remove now-dead onFullscreenPress/onCategorySearchPress/fullscreenButtonTestID
  props and branches from the shared header (unused after the redesign)
- Drop redundant throttleMs={1000} (LivePriceHeader already defaults to 1000)
- Add unit tests for the LivePriceHeader size variant and the header
  showAssetName layout (title/subtitle, market-list button)
- Use the exported MARKET_LIST_BUTTON selector in tests; remove unused
  category_search locale string
- Track market-list button with BUTTON_LOCATION.PERP_MARKET_DETAILS
  (ASSET_DETAILS is reserved for the magnifying-glass discovery entry)
- Add named FULLSCREEN_CHART_BUTTON selector; replace templated testID in
  the view and all test references
- Remove orphaned CATEGORY_SEARCH_BUTTON selector
- Centralize PERPS_COLLATERAL_SYMBOL in constants/perpsConfig
- Remove the now-unused category_search string from all locale files
- Assert the market-list analytics event and prove price/change/fullscreen
  render inside the MARKET_SUMMARY row
@codecov-commenter

codecov-commenter commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.51%. Comparing base (81c293e) to head (5599d1a).
⚠️ Report is 22 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #33024      +/-   ##
==========================================
+ Coverage   84.48%   84.51%   +0.02%     
==========================================
  Files        6003     6017      +14     
  Lines      157773   158222     +449     
  Branches    38368    38471     +103     
==========================================
+ Hits       133300   133721     +421     
- Misses      15307    15319      +12     
- Partials     9166     9182      +16     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Rename the header layout flag showAssetName -> useDetailLayout so the call
  site conveys that it switches the full header layout (title, subtitle,
  accessories, and relocating price/change), not just the asset name
- Update perps-metametrics-reference: asset_details is now the legacy
  magnifying-glass value; the market-list arrow reports perp_market_details
- Restore the translated locale files: string add/remove flows only through
  en.json (the Crowdin source per crowdin.yml); non-English locales are
  reconciled by Crowdin and fall back to English at runtime, matching the
  existing repo convention
- Rename the no-maxLeverage view test and scope its price/change assertions
  to the MARKET_SUMMARY row (price/change no longer live in the header)
@michalconsensys michalconsensys marked this pull request as ready for review July 9, 2026 11:01
@michalconsensys michalconsensys requested a review from a team as a code owner July 9, 2026 11:01
@github-actions github-actions Bot added the risk:medium AI analysis: medium risk label Jul 9, 2026

@aganglada aganglada 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.

Please fix MMDS deprecations

@aganglada

Copy link
Copy Markdown
Contributor
Simulator Screenshot - iPhone 17 - 2026-07-09 at 18 36 58 Please match the sizes / design on asset details

…recated MMDS APIs

Replace deprecated component-library `Text` in LivePriceHeader and the
deprecated `useHeaderStandardAnimated` in PerpsMarketDetailsView with the
`@metamask/design-system-react-native` equivalents, mapping variant/color
enums and using `fontWeight` for the medium-weight change text.
@github-actions github-actions Bot added size-L risk:low AI analysis: low risk and removed size-M risk:medium AI analysis: medium risk labels Jul 10, 2026
# Conflicts:
#	app/components/UI/Perps/Views/PerpsMarketDetailsView/PerpsMarketDetailsView.tsx
@github-actions

github-actions Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

🧪 Flaky unit test detection

Run history flaky detection

View recent run history

Historical failure rate is a hint, not proof — review each suggestion in context. See the flaky-test-detection skill for the full pattern reference and manual audit workflow.

Failures / runs sampled per window:

File 7d 15d 30d
app/components/UI/Perps/Views/PerpsMarketDetailsView/PerpsMarketDetailsView.test.tsx 0/102 0/173 0/368
app/components/UI/Perps/components/LivePriceDisplay/LivePriceHeader.test.tsx 0/102 0/173 0/368

AI-detected flaky patterns

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

  • J3 — Missing jest.resetAllMocks() — mockImplementation bleeds between tests (high)
    • The new test overrides usePerpsEventTracking via mockUsePerpsEventTrackingFn.mockImplementation(...). The outer beforeEach only calls jest.clearAllMocks(), which clears call counts and instances but does NOT reset mock implementations. jest.resetAllMocks() is needed to reset implementations. As a result, any test that runs after this one and relies on usePerpsEventTracking returning its default mock value will instead receive { track: mockTrack } (where mockTrack is a stale jest.fn() from the previous test). This causes order-dependent failures. The fix is to add jest.resetAllMocks() in the afterEach (or change clearAllMocks to resetAllMocks in beforeEach), or scope the override to only this test by restoring the original implementation at the end of the test.
    • Suggested fix in app/components/UI/Perps/Views/PerpsMarketDetailsView/PerpsMarketDetailsView.test.tsx:3649:
      -    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(
      -        // ...
      -      );
      -      // ...
      -    });
      +  afterEach(() => {
      +    jest.restoreAllMocks();
      +    jest.resetAllMocks(); // resets mockImplementation overrides set per-test
      +    mockComplianceGate.mockImplementation((action: () => Promise<unknown>) =>
      +      action(),
      +    );
      +    // ... rest of afterEach
      +  });
  • J7 — Non-deterministic data — live Date in mock fixture (medium)
    • new Date().toISOString() is evaluated at describe-block parse time (before any jest.useFakeTimers or jest.spyOn(Date, 'now') takes effect), producing a live wall-clock timestamp. This value differs from the pinned MOCK_NOW_MS constant used throughout the rest of the file. Any assertion that compares or depends on generatedAt being a specific value will be non-deterministic across CI runs. The fix is to derive generatedAt from the pinned constant MOCK_NOW_MS so it is always deterministic.
    • Suggested fix in app/components/UI/Perps/Views/PerpsMarketDetailsView/PerpsMarketDetailsView.test.tsx:4400:
      -    const mockReport = {
      -      asset: 'BTC',
      -      summary: 'BTC momentum is building with increased buying pressure.',
      -      sentiment: 'bullish',
      -      generatedAt: new Date().toISOString(),
      -    };
      +    const mockReport = {
      +      asset: 'BTC',
      +      summary: 'BTC momentum is building with increased buying pressure.',
      +      sentiment: 'bullish',
      +      generatedAt: new Date(MOCK_NOW_MS).toISOString(),
      +    };
  • J9 — Module-level mutable binding not reset in beforeEach (high)
    • mockRouteParams is a module-level mutable object. Multiple tests mutate its properties directly (e.g. mockRouteParams.market = undefined, mockRouteParams.monitoringIntent = { asset: 'BTC', monitor: 'orders' }). The outer beforeEach does not reset mockRouteParams to its default shape, so mutations from one test bleed into subsequent tests. For example, a test that sets mockRouteParams.market = undefined will cause the next test (which expects a valid market) to see undefined if test order changes. The fix is to reset mockRouteParams to its default value in beforeEach.
    • Suggested fix in app/components/UI/Perps/Views/PerpsMarketDetailsView/PerpsMarketDetailsView.test.tsx:320:
      -const mockRouteParams: {
      -  market?: PerpsMarketData;
      -  monitoringIntent?: {
      -    asset: string;
      -    monitor: 'orders' | 'positions' | 'both';
      -    // ...
      -  };
      -} = {
      -  // initial value
      -};
      +  beforeEach(() => {
      +    jest.clearAllMocks();
      +    jest.spyOn(Date, 'now').mockReturnValue(MOCK_NOW_MS);
      +    // Reset mockRouteParams to default before every test
      +    mockRouteParams.market = DEFAULT_MOCK_MARKET; // use the default market fixture
      +    mockRouteParams.monitoringIntent = undefined;
      +    // ... rest of beforeEach setup
      +  });

app/components/UI/Perps/components/LivePriceDisplay/LivePriceHeader.test.tsx

  • J7 — Non-deterministic data: Date.now() in mock fixtures (medium)
    • Every mock object passed to mockUsePerpsLivePrices.mockReturnValue(...) includes timestamp: Date.now(). This is called at test-execution time, so the value differs on every run. Although timestamp is not directly asserted in these tests today, it is part of the live data object consumed by the component. If the component ever branches on timestamp (e.g. staleness checks, display logic), or if a future assertion is added against it, the test will become non-deterministic. Pinning the timestamp to a fixed value is the safe, idiomatic approach and costs nothing. The same pattern appears on approximately 15 separate mockReturnValue calls throughout the file (lines ~52, 83, 97, 111, 125, 139, 153, 167, 181, 195, 209, 223, 237, 251, 265, 279, 293, 307, 321, 335, 349, 363, 377, 391, 405, 419, 433, 447).
    • Suggested fix in app/components/UI/Perps/components/LivePriceDisplay/LivePriceHeader.test.tsx:52:
      -      ETH: {
      -        symbol: 'ETH',
      -        price: '0',
      -        percentChange24h: '5',
      -        timestamp: Date.now(),
      -        isTradable: true,
      -      },
      +// At the top of the describe block, define a fixed timestamp constant:
      +const FIXED_TIMESTAMP = 1_700_000_000_000; // fixed epoch ms
      +
      +// Then replace every `timestamp: Date.now()` in mock objects with:
      +      ETH: {
      +        symbol: 'ETH',
      +        price: '0',
      +        percentChange24h: '5',
      +        timestamp: FIXED_TIMESTAMP,
      +        isTradable: true,
      +      },

This check is informational only and does not block merging.

@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.

The redesigned header is well scoped, preserves the compact orderbook layout, and includes solid analytics and test coverage. I left two nonblocking suggestions to improve touch accessibility and layout behavior with narrow screens or increased font sizes.

{showMarketListButton ? (
<ButtonIcon
iconName={IconName.ArrowDown}
size={ButtonIconSize.Sm}

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.

Would it make sense to keep the chevron visually small while expanding its interactive area with hitSlop? The current 24×24 dp target may be difficult to press reliably, particularly for users with limited motor precision.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

twClassName="px-4 pb-2"
testID={PerpsMarketDetailsViewSelectorsIDs.MARKET_SUMMARY}
>
<LivePriceHeader

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.

Could we give the price section room to shrink while keeping the fullscreen button fixed? Longer prices or increased system font sizes may overflow this row. A flexible wrapper plus defined truncation or wrapping behavior would make the layout more resilient.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Add hitSlop to the market-list chevron so its touch target reaches ~44dp
while staying visually small, and make the market summary price section
flexible/shrinkable with truncation so long prices or larger system font
sizes don't overflow the fixed fullscreen button.

@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 0584704. Configure here.

@github-actions github-actions Bot added risk:medium AI analysis: medium risk and removed risk:low AI analysis: low risk labels Jul 15, 2026
Fix a stale reference to the old templated `${HEADER}-fullscreen-button`
testID; the redesigned fullscreen button lives in the market summary row
under the named FULLSCREEN_CHART_BUTTON selector.
Match the asset overview screen for the prominent market-summary header:
stack the 24h change below the price and show both the absolute change
and the percentage (e.g. "+$3.57 (+0.21%)") using a medium-weight BodySm
line. The absolute change is derived from the current price and the 24h
percentage. The compact header keeps its inline, percentage-only layout.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokePerps, SmokeWalletPlatform, SmokeConfirmations
  • Selected Performance tags: @PerformancePreps
  • Risk Level: medium
  • AI Confidence: 85%
click to see 🤖 AI reasoning details

E2E Test Selection:
All 13 changed files are within the Perps feature area (app/components/UI/Perps/) plus supporting files (locales, docs). The changes represent a UI redesign of the PerpsMarketDetailsView header:

  1. PerpsMarketDetailsView.tsx - Refactored header layout: price/change display moved below the header into a new MARKET_SUMMARY section, fullscreen button relocated, analytics event names updated (MAGNIFYING_GLASS → MARKET_LIST, ASSET_DETAILS → PERP_MARKET_DETAILS).

  2. LivePriceHeader.tsx - Added size prop for compact vs. large display modes; updated color token names (TextColor.Default → TextColor.TextDefault, TextColor.Success → TextColor.SuccessDefault, etc.); added absolute 24h change calculation for large size.

  3. PerpsMarketInlineHeader.tsx - Significant refactor with new useDetailLayout prop; changed title/description rendering logic; removed fullscreen and category search buttons from header.

  4. PerpsMarketHeader.tsx - Interface changes: removed onFullscreenPress/onCategorySearchPress, added onMarketListPress/useDetailLayout.

  5. Perps.testIds.ts - Added new test IDs (MARKET_SUMMARY, FULLSCREEN_CHART_BUTTON), renamed CATEGORY_SEARCH_BUTTON → MARKET_LIST_BUTTON, added SUBTITLE.

  6. perpsConfig.ts - Added PERPS_COLLATERAL_SYMBOL constant.

  7. en.json - Updated locale strings (category_search → market_list, added perp_pair).

Tag selection rationale:

  • SmokePerps: Primary tag for perps functionality. The market details view header is a core part of the perps trading flow.
  • SmokeWalletPlatform: Per SmokePerps description, Perps is a section inside the Trending tab; changes to Perps views affect Trending/SmokeWalletPlatform.
  • SmokeConfirmations: Per SmokePerps description, when selecting SmokePerps, also select SmokeConfirmations since Add Funds deposits are on-chain transactions.

The changes are UI-only within the Perps feature and don't affect core wallet infrastructure, navigation, or other feature areas. No smoke test files directly reference the renamed test IDs (CATEGORY_SEARCH_BUTTON), so existing tests should not break.

Performance Test Selection:
The PerpsMarketDetailsView header was significantly redesigned with new layout components (Box, LivePriceHeader in large mode, ButtonIcon), new rendering logic for price/change display, and updated style calculations. These changes affect the rendering performance of the perps market details screen, which is covered by @PerformancePreps (perpetuals trading performance, covers perps market loading and position management). The LivePriceHeader now has additional computation (absoluteChange calculation) on each render cycle.

View GitHub Actions results

@sonarqubecloud

Copy link
Copy Markdown

@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) 9.87s @mm-perps-engineering-team 📹 Watch
Perps open position and close it Android Google Pixel 8 Pro (v14.0) 20.46s @mm-perps-engineering-team 📹 Watch

Branch: feat/perps-change-market-headers-TAT-3348 · Build: Normal · Commit: 1b482be · View full run

@michalconsensys michalconsensys added this pull request to the merge queue Jul 15, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 15, 2026
@aganglada aganglada added this pull request to the merge queue Jul 15, 2026
Merged via the queue into main with commit db49458 Jul 15, 2026
189 checks passed
@aganglada aganglada deleted the feat/perps-change-market-headers-TAT-3348 branch July 15, 2026 15:34
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 15, 2026
@metamask-ci metamask-ci Bot added the release-8.4.0 Issue or pull request that will be included in release 8.4.0 label Jul 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants