-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
feat(perps): scaffold pro mode market layout (PerpsProMarketView) #33560
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
ba74b18
feat(perps): scaffold pro mode market layout (PerpsProMarketView)
geositta 865a19a
chore: add files
geositta 4b77e3a
fix: followups to scaffolding
geositta 26e5b6a
fix: add layout test file and summary component
geositta f8e9b42
fix: orderbook width
geositta 1ebc35b
fix: rm layout config, and use fixed layout
geositta f511aed
feat(perps): add route validation fix
geositta File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
app/components/UI/Perps/Views/PerpsMarketDetailsRouter/PerpsMarketDetailsRouter.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import React from 'react'; | ||
| import { render } from '@testing-library/react-native'; | ||
| import PerpsMarketDetailsRouter from './PerpsMarketDetailsRouter'; | ||
| import { usePerpsProModeEnabled } from './usePerpsProModeEnabled'; | ||
|
|
||
| jest.mock('./usePerpsProModeEnabled'); | ||
|
|
||
| jest.mock('../PerpsProMarketView', () => { | ||
| const { View } = jest.requireActual('react-native'); | ||
| return { | ||
| __esModule: true, | ||
| default: () => <View testID="mock-pro-market-view" />, | ||
| }; | ||
| }); | ||
|
|
||
| jest.mock('../PerpsMarketDetailsView', () => { | ||
| const { View } = jest.requireActual('react-native'); | ||
| return { | ||
| __esModule: true, | ||
| default: () => <View testID="mock-lite-market-details-view" />, | ||
| }; | ||
| }); | ||
|
|
||
| const mockUsePerpsProModeEnabled = jest.mocked(usePerpsProModeEnabled); | ||
|
|
||
| describe('PerpsMarketDetailsRouter', () => { | ||
| afterEach(() => { | ||
| jest.clearAllMocks(); | ||
| }); | ||
|
|
||
| it('renders PerpsProMarketView when Pro mode is enabled', () => { | ||
| mockUsePerpsProModeEnabled.mockReturnValue(true); | ||
|
|
||
| const { getByTestId, queryByTestId } = render(<PerpsMarketDetailsRouter />); | ||
|
|
||
| expect(getByTestId('mock-pro-market-view')).toBeOnTheScreen(); | ||
| expect( | ||
| queryByTestId('mock-lite-market-details-view'), | ||
| ).not.toBeOnTheScreen(); | ||
| }); | ||
|
|
||
| it('renders PerpsMarketDetailsView when Pro mode is disabled', () => { | ||
| mockUsePerpsProModeEnabled.mockReturnValue(false); | ||
|
|
||
| const { getByTestId, queryByTestId } = render(<PerpsMarketDetailsRouter />); | ||
|
|
||
| expect(getByTestId('mock-lite-market-details-view')).toBeOnTheScreen(); | ||
| expect(queryByTestId('mock-pro-market-view')).not.toBeOnTheScreen(); | ||
| }); | ||
| }); |
20 changes: 20 additions & 0 deletions
20
app/components/UI/Perps/Views/PerpsMarketDetailsRouter/PerpsMarketDetailsRouter.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import React from 'react'; | ||
| import PerpsMarketDetailsView from '../PerpsMarketDetailsView'; | ||
| import PerpsProMarketView from '../PerpsProMarketView'; | ||
| import { usePerpsProModeEnabled } from './usePerpsProModeEnabled'; | ||
|
|
||
| /** | ||
| * Route component registered for `Routes.PERPS.MARKET_DETAILS`. | ||
| * | ||
| * Renders the Pro-mode market layout (`PerpsProMarketView`) when Pro mode is | ||
| * enabled, otherwise the lite `PerpsMarketDetailsView`. The route name and | ||
| * navigation params are identical for both modes; only the rendered component | ||
| * differs, so market-list navigation is unaffected. | ||
| */ | ||
| const PerpsMarketDetailsRouter: React.FC = () => { | ||
| const isProModeEnabled = usePerpsProModeEnabled(); | ||
|
|
||
| return isProModeEnabled ? <PerpsProMarketView /> : <PerpsMarketDetailsView />; | ||
| }; | ||
|
|
||
| export default PerpsMarketDetailsRouter; | ||
2 changes: 2 additions & 0 deletions
2
app/components/UI/Perps/Views/PerpsMarketDetailsRouter/index.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export { default } from './PerpsMarketDetailsRouter'; | ||
| export { usePerpsProModeEnabled } from './usePerpsProModeEnabled'; |
10 changes: 10 additions & 0 deletions
10
app/components/UI/Perps/Views/PerpsMarketDetailsRouter/usePerpsProModeEnabled.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { renderHook } from '@testing-library/react-native'; | ||
| import { usePerpsProModeEnabled } from './usePerpsProModeEnabled'; | ||
|
|
||
| describe('usePerpsProModeEnabled', () => { | ||
| it('returns false by default while Pro mode is not yet reachable (TAT-3551)', () => { | ||
| const { result } = renderHook(() => usePerpsProModeEnabled()); | ||
|
|
||
| expect(result.current).toBe(false); | ||
| }); | ||
| }); |
12 changes: 12 additions & 0 deletions
12
app/components/UI/Perps/Views/PerpsMarketDetailsRouter/usePerpsProModeEnabled.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| /** | ||
| * Temporary gate for the Perps Pro-mode layout. | ||
| * | ||
| * TODO(TAT-3551): replace this placeholder once the mobile feature-flag and | ||
|
Check warning on line 4 in app/components/UI/Perps/Views/PerpsMarketDetailsRouter/usePerpsProModeEnabled.ts
|
||
| * mode selectors land. The Pro view should render only when the feature flag | ||
| * is enabled and the persisted controller mode is `PerpsMode.Pro`. Until then | ||
| * this always returns `false`, so every user keeps the lite | ||
| * `PerpsMarketDetailsView` and the Pro layout is not user-reachable. | ||
| * | ||
| * @returns Whether the Perps Pro-mode layout is enabled for the current user. | ||
| */ | ||
| export const usePerpsProModeEnabled = (): boolean => false; | ||
16 changes: 16 additions & 0 deletions
16
app/components/UI/Perps/Views/PerpsProMarketView/PerpsProMarketView.styles.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { StyleSheet } from 'react-native'; | ||
| import type { Theme } from '../../../../../util/theme/models'; | ||
|
|
||
| export const createStyles = ({ theme }: { theme: Theme }) => | ||
| StyleSheet.create({ | ||
| container: { | ||
| flex: 1, | ||
| backgroundColor: theme.colors.background.default, | ||
| }, | ||
| scrollView: { | ||
| flex: 1, | ||
| }, | ||
| scrollContent: { | ||
| paddingBottom: 16, | ||
| }, | ||
| }); |
141 changes: 141 additions & 0 deletions
141
app/components/UI/Perps/Views/PerpsProMarketView/PerpsProMarketView.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| import React from 'react'; | ||
| import { within } from '@testing-library/react-native'; | ||
| import PerpsProMarketView from './'; | ||
| import renderWithProvider from '../../../../../util/test/renderWithProvider'; | ||
| import { backgroundState } from '../../../../../util/test/initial-root-state'; | ||
| import { PerpsProMarketViewSelectorsIDs } from '../../Perps.testIds'; | ||
|
|
||
| interface MockRouteParams { | ||
| market?: { symbol: string }; | ||
| } | ||
|
|
||
| let mockRouteParams: MockRouteParams | undefined = { | ||
| market: { symbol: 'BTC' }, | ||
| }; | ||
|
|
||
| jest.mock('@react-navigation/native', () => { | ||
| const actualNav = jest.requireActual('@react-navigation/native'); | ||
| return { | ||
| ...actualNav, | ||
| useRoute: () => ({ params: mockRouteParams }), | ||
| }; | ||
| }); | ||
|
|
||
| const renderView = () => | ||
| renderWithProvider(<PerpsProMarketView />, { | ||
| state: { engine: { backgroundState } }, | ||
| }); | ||
|
|
||
| describe('PerpsProMarketView', () => { | ||
| beforeEach(() => { | ||
| mockRouteParams = { market: { symbol: 'BTC' } }; | ||
| }); | ||
|
|
||
| it.each([ | ||
| ['params', undefined], | ||
| ['market', {}], | ||
| ['symbol', { market: { symbol: '' } }], | ||
| ] as const)( | ||
| 'renders the error state when route %s are invalid', | ||
| (_missingField, params) => { | ||
| mockRouteParams = params; | ||
|
|
||
| const { getByTestId, queryByTestId, getByText } = renderView(); | ||
|
|
||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.ERROR), | ||
| ).toBeOnTheScreen(); | ||
| expect( | ||
| getByText('Market data not found. Please go back and try again.'), | ||
| ).toBeOnTheScreen(); | ||
| expect( | ||
| queryByTestId(PerpsProMarketViewSelectorsIDs.CONTAINER), | ||
| ).not.toBeOnTheScreen(); | ||
| }, | ||
| ); | ||
|
|
||
| it('renders the screen inside every safe-area edge', () => { | ||
| const { getByTestId } = renderView(); | ||
|
|
||
| expect(getByTestId(PerpsProMarketViewSelectorsIDs.CONTAINER)).toHaveProp( | ||
| 'edges', | ||
| ['top', 'bottom', 'left', 'right'], | ||
| ); | ||
| }); | ||
|
|
||
| it('renders every top-level scaffold slot', () => { | ||
| const { getByTestId } = renderView(); | ||
|
|
||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.HEADER), | ||
| ).toBeOnTheScreen(); | ||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.MARKET_SUMMARY), | ||
| ).toBeOnTheScreen(); | ||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.CHART_PANEL), | ||
| ).toBeOnTheScreen(); | ||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.STATS_BAR), | ||
| ).toBeOnTheScreen(); | ||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.LAYOUT), | ||
| ).toBeOnTheScreen(); | ||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.ORDER_FORM_PANEL), | ||
| ).toBeOnTheScreen(); | ||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.ORDER_BOOK_PANEL), | ||
| ).toBeOnTheScreen(); | ||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.POSITIONS_PANEL), | ||
| ).toBeOnTheScreen(); | ||
| }); | ||
|
|
||
| it('keeps the header fixed while the market summary scrolls', () => { | ||
| const { getByTestId } = renderView(); | ||
|
|
||
| const scrollView = getByTestId(PerpsProMarketViewSelectorsIDs.SCROLL_VIEW); | ||
|
|
||
| expect( | ||
| within(scrollView).queryByTestId(PerpsProMarketViewSelectorsIDs.HEADER), | ||
| ).not.toBeOnTheScreen(); | ||
| expect( | ||
| within(scrollView).getByTestId( | ||
| PerpsProMarketViewSelectorsIDs.MARKET_SUMMARY, | ||
| ), | ||
| ).toBeOnTheScreen(); | ||
| }); | ||
|
|
||
| it('shows the asset symbol from route params in the header', () => { | ||
| const { getByTestId } = renderView(); | ||
|
|
||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.HEADER_SYMBOL), | ||
| ).toHaveTextContent(/^BTC$/); | ||
| }); | ||
|
|
||
| it('removes the HIP-3 dex prefix from the header symbol', () => { | ||
| mockRouteParams = { market: { symbol: 'xyz:TSLA' } }; | ||
|
|
||
| const { getByTestId } = renderView(); | ||
|
|
||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.HEADER_SYMBOL), | ||
| ).toHaveTextContent(/^TSLA$/); | ||
| }); | ||
|
|
||
| it('uses the Figma shell heights', () => { | ||
| const { getByTestId } = renderView(); | ||
|
|
||
| expect(getByTestId(PerpsProMarketViewSelectorsIDs.HEADER)).toHaveStyle({ | ||
| height: 64, | ||
| }); | ||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.MARKET_SUMMARY), | ||
| ).toHaveStyle({ height: 76 }); | ||
| expect( | ||
| getByTestId(PerpsProMarketViewSelectorsIDs.CHART_CONTENT), | ||
| ).toHaveStyle({ height: 344 }); | ||
| }); | ||
| }); |
88 changes: 88 additions & 0 deletions
88
app/components/UI/Perps/Views/PerpsProMarketView/PerpsProMarketView.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| import { | ||
| Box, | ||
| SectionDivider, | ||
| Text, | ||
| TextColor, | ||
| TextVariant, | ||
| } from '@metamask/design-system-react-native'; | ||
| import { getPerpsDisplaySymbol } from '@metamask/perps-controller'; | ||
| import { useRoute, type RouteProp } from '@react-navigation/native'; | ||
| import React from 'react'; | ||
| import { ScrollView } from 'react-native'; | ||
| import { SafeAreaView } from 'react-native-safe-area-context'; | ||
| import { strings } from '../../../../../../locales/i18n'; | ||
| import { useStyles } from '../../../../../component-library/hooks'; | ||
| import { PerpsProMarketViewSelectorsIDs } from '../../Perps.testIds'; | ||
| import type { PerpsStackParamList } from '../../types/navigation'; | ||
| import PerpsProChartPanel from './components/PerpsProChartPanel'; | ||
| import PerpsProMarketHeader from './components/PerpsProMarketHeader'; | ||
| import PerpsProMarketLayout from './components/PerpsProMarketLayout'; | ||
| import PerpsProMarketSummary from './components/PerpsProMarketSummary'; | ||
| import PerpsProOrderBookPanel from './components/PerpsProOrderBookPanel'; | ||
| import PerpsProOrderFormPanel from './components/PerpsProOrderFormPanel'; | ||
| import PerpsProPositionsPanel from './components/PerpsProPositionsPanel'; | ||
| import PerpsProStatsBar from './components/PerpsProStatsBar'; | ||
| import { createStyles } from './PerpsProMarketView.styles'; | ||
|
|
||
| /** | ||
| * Pro-mode replacement for `PerpsMarketDetailsView`. | ||
| * | ||
| * Scaffold only: lays out the full Pro trading screen (header, chart, stats | ||
| * bar, two-column order form / order book, and positions/orders section) as | ||
| * placeholder containers matching Figma node 10041:12979. Each panel can be | ||
| * populated by its owning capability without changing the top-level layout. | ||
| */ | ||
| const PerpsProMarketView = () => { | ||
| const { styles } = useStyles(createStyles, {}); | ||
| const route = | ||
| useRoute<RouteProp<PerpsStackParamList, 'PerpsMarketDetails'>>(); | ||
| const market = route.params?.market; | ||
|
|
||
| if (!market?.symbol) { | ||
| return ( | ||
| <SafeAreaView | ||
| style={styles.container} | ||
| edges={['top', 'bottom', 'left', 'right']} | ||
| > | ||
| <Box | ||
| twClassName="flex-1 items-center justify-center px-4" | ||
| testID={PerpsProMarketViewSelectorsIDs.ERROR} | ||
| > | ||
| <Text variant={TextVariant.BodySm} color={TextColor.ErrorDefault}> | ||
| {strings('perps.market.details.error_message')} | ||
| </Text> | ||
| </Box> | ||
| </SafeAreaView> | ||
| ); | ||
| } | ||
|
|
||
| const symbol = getPerpsDisplaySymbol(market.symbol); | ||
|
|
||
| return ( | ||
| <SafeAreaView | ||
| style={styles.container} | ||
| edges={['top', 'bottom', 'left', 'right']} | ||
| testID={PerpsProMarketViewSelectorsIDs.CONTAINER} | ||
| > | ||
| <PerpsProMarketHeader symbol={symbol} /> | ||
| <ScrollView | ||
| style={styles.scrollView} | ||
| contentContainerStyle={styles.scrollContent} | ||
| testID={PerpsProMarketViewSelectorsIDs.SCROLL_VIEW} | ||
| showsVerticalScrollIndicator={false} | ||
| > | ||
| <PerpsProMarketSummary /> | ||
| <PerpsProChartPanel /> | ||
| <PerpsProStatsBar /> | ||
| <PerpsProMarketLayout | ||
| orderForm={<PerpsProOrderFormPanel />} | ||
| orderBook={<PerpsProOrderBookPanel />} | ||
| /> | ||
| <SectionDivider /> | ||
| <PerpsProPositionsPanel /> | ||
| </ScrollView> | ||
| </SafeAreaView> | ||
| ); | ||
| }; | ||
|
|
||
| export default PerpsProMarketView; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we change the hook to send the mode and...
wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to keep the boolean hook because the router has a binary, fail safe decision and 3558 needs to merge before the mode API lands. Once TAT-3551 merges, the hook will resolve featureFlag && mode === Pro. I think a mode to element map adds indirection without improving the two case routing currently.