diff --git a/ui/components/app/perps/order-book/order-book-config-modal.tsx b/ui/components/app/perps/order-book/order-book-config-modal.tsx index 6620020d59fe..2efda0eac07e 100644 --- a/ui/components/app/perps/order-book/order-book-config-modal.tsx +++ b/ui/components/app/perps/order-book/order-book-config-modal.tsx @@ -44,9 +44,10 @@ import type { const PILL_BASE_CLASS = 'flex items-center justify-center rounded-full py-3 px-4 cursor-pointer transition-colors duration-150 border'; -const PILL_SELECTED_CLASS = 'bg-transparent border-muted'; + +const PILL_SELECTED_CLASS = 'bg-muted border-transparent'; const PILL_UNSELECTED_CLASS = - 'bg-muted border-transparent hover:bg-muted-hover'; + 'bg-transparent border-muted hover:bg-muted-hover'; type PillProps = { label: string; diff --git a/ui/pages/perps/perps-order-entry-page.test.tsx b/ui/pages/perps/perps-order-entry-page.test.tsx index 533b368f685e..9e9fcaef12f7 100644 --- a/ui/pages/perps/perps-order-entry-page.test.tsx +++ b/ui/pages/perps/perps-order-entry-page.test.tsx @@ -712,6 +712,43 @@ describe('PerpsOrderEntryPage', () => { expect(divider).toHaveAttribute('aria-valuenow', '33'); }); + it('mounts the order book already open when the persisted preference is expanded', () => { + const state = createMockState(); + const store = mockStore({ + ...state, + metamask: { + ...state.metamask, + proLayoutPreferences: { orderBookExpanded: true }, + }, + }); + renderWithProvider(, store); + + expect(screen.getByTestId('perps-order-book-toggle')).toHaveAttribute( + 'aria-pressed', + 'true', + ); + expect(screen.getByTestId('perps-order-book')).toBeInTheDocument(); + }); + + it('persists the open state when the order book is toggled', () => { + const store = mockStore(createMockState()); + renderWithProvider(, store); + + const toggle = screen.getByTestId('perps-order-book-toggle'); + + fireEvent.click(toggle); + expect(mockSubmitRequestToBackground).toHaveBeenCalledWith( + 'perpsSetProLayoutPreferences', + [{ orderBookExpanded: true }], + ); + + fireEvent.click(toggle); + expect(mockSubmitRequestToBackground).toHaveBeenCalledWith( + 'perpsSetProLayoutPreferences', + [{ orderBookExpanded: false }], + ); + }); + it('resizes the split within bounds using the keyboard', () => { const store = mockStore(createMockStateWithOrderBookPosition('right')); renderWithProvider(, store); diff --git a/ui/pages/perps/perps-order-entry-page.tsx b/ui/pages/perps/perps-order-entry-page.tsx index 7681588eaf53..684353aca8c7 100644 --- a/ui/pages/perps/perps-order-entry-page.tsx +++ b/ui/pages/perps/perps-order-entry-page.tsx @@ -72,6 +72,7 @@ import { selectPerpsIsTestnet, selectPerpsActiveProvider, selectOrderBookPosition, + selectOrderBookExpanded, } from '../../selectors/perps-controller'; import { CandlePeriod, @@ -301,7 +302,10 @@ const PerpsOrderEntryPage = () => { const { buildTrackingData, buildTpslTrackingData, setFlowAttribution } = usePerpsAttribution(); const [isGeoBlockModalOpen, setIsGeoBlockModalOpen] = useState(false); - const [isOrderBookOpen, setIsOrderBookOpen] = useState(false); + const persistedOrderBookExpanded = useSelector(selectOrderBookExpanded); + const [isOrderBookOpen, setIsOrderBookOpen] = useState( + persistedOrderBookExpanded, + ); const [orderBookWidthPct, setOrderBookWidthPct] = useState( ORDER_BOOK_DEFAULT_WIDTH_PCT, ); @@ -1447,6 +1451,11 @@ const PerpsOrderEntryPage = () => { const handleToggleOrderBook = useCallback(() => { const next = !isOrderBookOpen; setIsOrderBookOpen(next); + submitRequestToBackground('perpsSetProLayoutPreferences', [ + { orderBookExpanded: next }, + ]).catch((error) => + console.error('Failed to persist order book open state', error), + ); // Tracking is a side effect and must run outside the state updater (updaters // must be pure and may be invoked more than once). Specific open/close values // keep dark-launch open-rate measurable (generic TAP cannot). diff --git a/ui/selectors/perps-controller.test.ts b/ui/selectors/perps-controller.test.ts index 1f7684302602..3f559c370bf8 100644 --- a/ui/selectors/perps-controller.test.ts +++ b/ui/selectors/perps-controller.test.ts @@ -31,6 +31,7 @@ import { selectPerpsShouldShowDepositToast, selectProLayoutPreferences, selectOrderBookPosition, + selectOrderBookExpanded, } from './perps-controller'; function buildState(overrides: Record = {}) { @@ -855,4 +856,21 @@ describe('perps-controller selectors', () => { ).toBe('left'); }); }); + + describe('selectOrderBookExpanded', () => { + it('returns the persisted open state', () => { + expect( + selectOrderBookExpanded( + buildState({ proLayoutPreferences: { orderBookExpanded: true } }), + ), + ).toBe(true); + }); + + it('defaults to closed', () => { + expect(selectOrderBookExpanded(buildState())).toBe(false); + expect( + selectOrderBookExpanded(buildState({ proLayoutPreferences: {} })), + ).toBe(false); + }); + }); }); diff --git a/ui/selectors/perps-controller.ts b/ui/selectors/perps-controller.ts index aa07ca8f1081..9e616c421c95 100644 --- a/ui/selectors/perps-controller.ts +++ b/ui/selectors/perps-controller.ts @@ -239,5 +239,17 @@ export const selectOrderBookPosition = (state: PerpsState) => state.metamask.proLayoutPreferences?.orderBookPosition ?? DEFAULT_PRO_LAYOUT_PREFERENCES.orderBookPosition; +/** + * Whether the order book panel was left open. Global across markets (the + * preference object is flat, not per-market), so the panel opens in the same + * state on every symbol. + * + * @param state - Perps controller state. + * @returns True when the panel should start open. + */ +export const selectOrderBookExpanded = (state: PerpsState) => + state.metamask.proLayoutPreferences?.orderBookExpanded ?? + DEFAULT_PRO_LAYOUT_PREFERENCES.orderBookExpanded; + export const selectPerpsTradeConfigurations = (state: PerpsState) => state.metamask.tradeConfigurations ?? EMPTY_TRADE_CONFIGURATIONS;