diff --git a/apps/trading/pages/markets/[marketId].page.tsx b/apps/trading/pages/markets/[marketId].page.tsx
index fd8d9428e..41e43c891 100644
--- a/apps/trading/pages/markets/[marketId].page.tsx
+++ b/apps/trading/pages/markets/[marketId].page.tsx
@@ -1,18 +1,10 @@
import { gql } from '@apollo/client';
-import AutoSizer from 'react-virtualized-auto-sizer';
-import classNames from 'classnames';
import { useRouter } from 'next/router';
-import React, {
- Children,
- isValidElement,
- ReactNode,
- useCallback,
- useEffect,
- useState,
-} from 'react';
+import React, { useEffect, useState } from 'react';
import debounce from 'lodash/debounce';
-import { Market, MarketVariables, Market_market } from './__generated__/Market';
+import { Market, MarketVariables } from './__generated__/Market';
import { PageQueryContainer } from '../../components/page-query-container';
+import { TradeGrid, TradePanels } from './trade-grid';
// Top level page query
const MARKET_QUERY = gql`
@@ -56,239 +48,6 @@ const MarketPage = () => {
export default MarketPage;
-interface TradeGridProps {
- market: Market_market;
-}
-
-const TradeGrid = ({ market }: TradeGridProps) => {
- const wrapperClasses = classNames(
- 'h-full max-h-full',
- 'grid gap-[1px] grid-cols-[1fr_325px_325px] grid-rows-[min-content_1fr_200px]',
- 'bg-neutral-200',
- 'text-ui'
- );
- return (
-
-
- Market: {market.name}
-
-
-
-
-
-
-
-
-
-
- {JSON.stringify(market.trades, null, 2)}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-interface TradeGridChildProps {
- children: ReactNode;
- className?: string;
-}
-
-const TradeGridChild = ({ children, className }: TradeGridChildProps) => {
- const gridChildClasses = classNames('bg-white', className);
- return (
-
-
- {({ width, height }) => (
-
- {children}
-
- )}
-
-
- );
-};
-
-interface GridTabsProps {
- children: ReactNode;
- group: string;
-}
-
-const GridTabs = ({ children, group }: GridTabsProps) => {
- const { query, asPath, replace } = useRouter();
- const [activeTab, setActiveTab] = useState(() => {
- if (query[group]) {
- return query[group];
- }
-
- // Default to first tab
- return children[0].props.name;
- });
-
- // Using replace inside an effect causes a render loop. Seems like its not using useCallback
- // eslint-disable-next-line
- const safeReplace = useCallback((path: string) => replace(path), []);
-
- useEffect(() => {
- const [url, queryString] = asPath.split('?');
- const searchParams = new URLSearchParams(queryString);
- searchParams.set(group, activeTab as string);
- safeReplace(`${url}?${searchParams.toString()}`);
- }, [activeTab, group, asPath, safeReplace]);
-
- return (
-
- {/* the tabs */}
-
- {Children.map(children, (child) => {
- if (!isValidElement(child)) return null;
- const isActive = activeTab === child.props.name;
- const buttonClass = classNames(
- 'py-4',
- 'px-12',
- 'border-t border-neutral-200',
- 'capitalize',
- {
- 'text-vega-pink': isActive,
- 'bg-white': isActive,
- }
- );
- return (
-
- );
- })}
-
- {/* the content */}
-
- {Children.map(children, (child) => {
- if (isValidElement(child) && activeTab === child.props.name) {
- return (
-
- {child.props.children}
-
- );
- }
- return null;
- })}
-
-
- );
-};
-
-interface GridTabProps {
- children: ReactNode;
- name: string;
-}
-
-const GridTab = ({ children }: GridTabProps) => {
- return {children}
;
-};
-
-///// SMALL SCREENS ///////
-
-type View = keyof typeof Views;
-
-interface TradePanelsProps {
- market: Market_market;
-}
-
-const TradePanels = ({ market }: TradePanelsProps) => {
- const [view, setView] = React.useState('chart');
-
- const renderView = () => {
- const Component = Views[view];
-
- if (!Component) {
- throw new Error(`No component for view: ${view}`);
- }
-
- return ;
- };
-
- return (
-
-
- Market: {market.name}
-
-
-
- {({ width, height }) => (
- {renderView()}
- )}
-
-
-
- {Object.keys(Views).map((key: View) => {
- const className = classNames(
- 'p-8',
- 'border-t',
- 'border-neutral-200',
- 'capitalize',
- {
- 'text-vega-pink': view === key,
- 'bg-white': view === key,
- }
- );
- return (
-
- );
- })}
-
-
- );
-};
-
-const Chart = () => TODO: Chart
;
-const Ticket = () => TODO: Ticket
;
-const Orderbook = () => TODO: Orderbook
;
-const Orders = () => TODO: Orders
;
-const Positions = () => TODO: Positions
;
-const Collateral = () => TODO: Collateral
;
-
-const Views = {
- chart: Chart,
- ticket: Ticket,
- orderbook: Orderbook,
- orders: Orders,
- positions: Positions,
- collateral: Collateral,
-};
-
const useWindowSize = () => {
const [windowSize, setWindowSize] = useState(() => {
if (typeof window !== 'undefined') {
diff --git a/apps/trading/pages/markets/grid-tabs.tsx b/apps/trading/pages/markets/grid-tabs.tsx
new file mode 100644
index 000000000..39860a91f
--- /dev/null
+++ b/apps/trading/pages/markets/grid-tabs.tsx
@@ -0,0 +1,140 @@
+import classNames from 'classnames';
+import { useRouter } from 'next/router';
+import {
+ Children,
+ isValidElement,
+ ReactNode,
+ useCallback,
+ useEffect,
+ useState,
+} from 'react';
+
+interface GridTabsProps {
+ children: ReactNode;
+ group: string;
+}
+
+export const GridTabs = ({ children, group }: GridTabsProps) => {
+ const { query, asPath, replace } = useRouter();
+ const [activeTab, setActiveTab] = useState(() => {
+ if (query[group]) {
+ return query[group];
+ }
+
+ // Default to first tab
+ return children[0].props.name;
+ });
+
+ // Using replace inside an effect causes a render loop. Seems like its not using useCallback
+ // eslint-disable-next-line
+ const safeReplace = useCallback((path: string) => replace(path), []);
+
+ // Update the query string in the url when the active tab changes
+ // uses group property as the query stirng key
+ useEffect(() => {
+ const [url, queryString] = asPath.split('?');
+ const searchParams = new URLSearchParams(queryString);
+ searchParams.set(group, activeTab as string);
+ safeReplace(`${url}?${searchParams.toString()}`);
+ }, [activeTab, group, asPath, safeReplace]);
+
+ return (
+
+ {/* the tabs */}
+
+ {Children.map(children, (child) => {
+ if (isValidElement(child)) {
+ return (
+ setActiveTab(child.props.name)}
+ />
+ );
+ }
+
+ return null;
+ })}
+
+ {/* the content */}
+
+ {Children.map(children, (child) => {
+ if (isValidElement(child) && activeTab === child.props.name) {
+ return (
+
+ {child.props.children}
+
+ );
+ }
+
+ return null;
+ })}
+
+
+ );
+};
+
+interface GridTabProps {
+ children: ReactNode;
+ name: string;
+}
+
+export const GridTab = ({ children }: GridTabProps) => {
+ return {children}
;
+};
+
+interface GridTabControlProps {
+ group: string;
+ name: string;
+ isActive: boolean;
+ onClick: () => void;
+}
+
+const GridTabControl = ({
+ group,
+ name,
+ isActive,
+ onClick,
+}: GridTabControlProps) => {
+ const buttonClass = classNames(
+ 'py-4',
+ 'px-12',
+ 'border-t border-neutral-200',
+ 'capitalize',
+ {
+ 'text-vega-pink': isActive,
+ 'bg-white': isActive,
+ }
+ );
+ return (
+
+ );
+};
+
+interface GridTabPanelProps {
+ group: string;
+ name: string;
+ children: ReactNode;
+}
+
+const GridTabPanel = ({ group, name, children }: GridTabPanelProps) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/apps/trading/pages/markets/trade-grid.tsx b/apps/trading/pages/markets/trade-grid.tsx
new file mode 100644
index 000000000..7e985ac5f
--- /dev/null
+++ b/apps/trading/pages/markets/trade-grid.tsx
@@ -0,0 +1,133 @@
+import classNames from 'classnames';
+import AutoSizer from 'react-virtualized-auto-sizer';
+import { useState, ReactNode } from 'react';
+import { Market_market } from './__generated__/Market';
+import { Views } from './trading-components';
+import { GridTab, GridTabs } from './grid-tabs';
+
+interface TradeGridProps {
+ market: Market_market;
+}
+
+export const TradeGrid = ({ market }: TradeGridProps) => {
+ const wrapperClasses = classNames(
+ 'h-full max-h-full',
+ 'grid gap-[1px] grid-cols-[1fr_325px_325px] grid-rows-[min-content_1fr_200px]',
+ 'bg-neutral-200',
+ 'text-ui'
+ );
+ return (
+
+
+ Market: {market.name}
+
+
+
+
+
+
+
+
+
+
+ {JSON.stringify(market.trades, null, 2)}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+interface TradeGridChildProps {
+ children: ReactNode;
+ className?: string;
+}
+
+const TradeGridChild = ({ children, className }: TradeGridChildProps) => {
+ const gridChildClasses = classNames('bg-white', className);
+ return (
+
+
+ {({ width, height }) => (
+
+ {children}
+
+ )}
+
+
+ );
+};
+
+type View = keyof typeof Views;
+
+interface TradePanelsProps {
+ market: Market_market;
+}
+
+export const TradePanels = ({ market }: TradePanelsProps) => {
+ const [view, setView] = useState('chart');
+
+ const renderView = () => {
+ const Component = Views[view];
+
+ if (!Component) {
+ throw new Error(`No component for view: ${view}`);
+ }
+
+ return ;
+ };
+
+ return (
+
+
+ Market: {market.name}
+
+
+
+ {({ width, height }) => (
+ {renderView()}
+ )}
+
+
+
+ {Object.keys(Views).map((key: View) => {
+ const className = classNames(
+ 'p-8',
+ 'border-t',
+ 'border-neutral-200',
+ 'capitalize',
+ {
+ 'text-vega-pink': view === key,
+ 'bg-white': view === key,
+ }
+ );
+ return (
+
+ );
+ })}
+
+
+ );
+};
diff --git a/apps/trading/pages/markets/trading-components.tsx b/apps/trading/pages/markets/trading-components.tsx
new file mode 100644
index 000000000..73b00c453
--- /dev/null
+++ b/apps/trading/pages/markets/trading-components.tsx
@@ -0,0 +1,15 @@
+export const Chart = () => TODO: Chart
;
+export const Ticket = () => TODO: Ticket
;
+export const Orderbook = () => TODO: Orderbook
;
+export const Orders = () => TODO: Orders
;
+export const Positions = () => TODO: Positions
;
+export const Collateral = () => TODO: Collateral
;
+
+export const Views = {
+ chart: Chart,
+ ticket: Ticket,
+ orderbook: Orderbook,
+ orders: Orders,
+ positions: Positions,
+ collateral: Collateral,
+};