feat(trading): market view persistent pane sizes (#3537)
This commit is contained in:
parent
4a4cdaa2b8
commit
7a99ded8e9
@ -27,7 +27,10 @@ import { NO_MARKET } from './constants';
|
||||
import { LiquidityContainer } from '../liquidity/liquidity';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
usePaneLayout,
|
||||
useScreenDimensions,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
useMarketClickHandler,
|
||||
useMarketLiquidityClickHandler,
|
||||
@ -83,15 +86,20 @@ interface BottomPanelProps {
|
||||
|
||||
const MarketBottomPanel = memo(
|
||||
({ marketId, pinnedAsset }: BottomPanelProps) => {
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'bottom' });
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||
|
||||
return 'xxxl' === screenSize ? (
|
||||
<ResizableGrid proportionalLayout minSize={200}>
|
||||
<ResizableGrid
|
||||
proportionalLayout
|
||||
minSize={200}
|
||||
onChange={handleOnLayoutChange}
|
||||
>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize="50%"
|
||||
preferredSize={sizes[0] || '50%'}
|
||||
minSize={50}
|
||||
>
|
||||
<TradeGridChild>
|
||||
@ -119,7 +127,7 @@ const MarketBottomPanel = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize="50%"
|
||||
preferredSize={sizes[1] || '50%'}
|
||||
minSize={50}
|
||||
>
|
||||
<TradeGridChild>
|
||||
@ -186,22 +194,29 @@ MarketBottomPanel.displayName = 'MarketBottomPanel';
|
||||
const MainGrid = memo(
|
||||
({
|
||||
marketId,
|
||||
onSelect,
|
||||
pinnedAsset,
|
||||
}: {
|
||||
marketId: string;
|
||||
onSelect: (marketId: string, metaKey?: boolean) => void;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'top' });
|
||||
const [sizesMiddle, handleOnMiddleLayoutChange] = usePaneLayout({
|
||||
id: 'middle',
|
||||
});
|
||||
|
||||
return (
|
||||
<ResizableGrid vertical>
|
||||
<ResizableGrid vertical onChange={handleOnLayoutChange}>
|
||||
<ResizableGridPanel minSize={75} priority={LayoutPriority.High}>
|
||||
<ResizableGrid proportionalLayout={false} minSize={200}>
|
||||
<ResizableGrid
|
||||
proportionalLayout={false}
|
||||
minSize={200}
|
||||
onChange={handleOnMiddleLayoutChange}
|
||||
>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.High}
|
||||
minSize={200}
|
||||
preferredSize="50%"
|
||||
preferredSize={sizesMiddle[0] || '50%'}
|
||||
>
|
||||
<TradeGridChild>
|
||||
<Tabs storageKey="console-trade-grid-main-left">
|
||||
@ -219,7 +234,7 @@ const MainGrid = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize={330}
|
||||
preferredSize={sizesMiddle[1] || 330}
|
||||
minSize={300}
|
||||
>
|
||||
<TradeGridChild>
|
||||
@ -238,7 +253,7 @@ const MainGrid = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize={430}
|
||||
preferredSize={sizesMiddle[2] || 430}
|
||||
minSize={200}
|
||||
>
|
||||
<TradeGridChild>
|
||||
@ -256,7 +271,7 @@ const MainGrid = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize="25%"
|
||||
preferredSize={sizes[1] || '25%'}
|
||||
minSize={50}
|
||||
>
|
||||
<MarketBottomPanel marketId={marketId} pinnedAsset={pinnedAsset} />
|
||||
@ -278,11 +293,7 @@ export const TradeGrid = ({
|
||||
<TradeMarketHeader market={market} onSelect={onSelect} />
|
||||
<OracleBanner marketId={market?.id || ''} />
|
||||
</div>
|
||||
<MainGrid
|
||||
marketId={market?.id || ''}
|
||||
onSelect={onSelect}
|
||||
pinnedAsset={pinnedAsset}
|
||||
/>
|
||||
<MainGrid marketId={market?.id || ''} pinnedAsset={pinnedAsset} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ import { WithdrawalsContainer } from './withdrawals-container';
|
||||
import { FillsContainer } from '@vegaprotocol/fills';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { usePaneLayout } from '@vegaprotocol/react-helpers';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
import { DepositsContainer } from './deposits-container';
|
||||
import { LayoutPriority } from 'allotment';
|
||||
@ -34,11 +35,11 @@ export const Portfolio = () => {
|
||||
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
|
||||
const wrapperClasses = 'h-full max-h-full flex flex-col';
|
||||
return (
|
||||
<div className={wrapperClasses}>
|
||||
<ResizableGrid vertical>
|
||||
<ResizableGrid vertical onChange={handleOnLayoutChange}>
|
||||
<ResizableGridPanel minSize={75}>
|
||||
<PortfolioGridChild>
|
||||
<Tabs storageKey="console-portfolio-top">
|
||||
@ -78,7 +79,7 @@ export const Portfolio = () => {
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize={300}
|
||||
preferredSize={sizes[1] || 300}
|
||||
minSize={50}
|
||||
>
|
||||
<PortfolioGridChild>
|
||||
|
@ -17,3 +17,4 @@ export * from './use-yesterday';
|
||||
export * from './use-previous';
|
||||
export * from './use-logger';
|
||||
export * from './use-bottom-placeholder';
|
||||
export * from './use-pane-layout';
|
||||
|
19
libs/react-helpers/src/hooks/use-pane-layout.spec.ts
Normal file
19
libs/react-helpers/src/hooks/use-pane-layout.spec.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { renderHook, act, waitFor } from '@testing-library/react';
|
||||
import { usePaneLayout } from './use-pane-layout';
|
||||
|
||||
describe('usePaneLayout', () => {
|
||||
it('should return proper values', () => {
|
||||
const ret = renderHook(() => usePaneLayout({ id: 'testid' }));
|
||||
expect(ret.result.current[0]).toStrictEqual([]);
|
||||
expect(ret.result.current[1]).toStrictEqual(expect.any(Function));
|
||||
});
|
||||
it('setter should change value', async () => {
|
||||
const ret = renderHook(() => usePaneLayout({ id: 'testid' }));
|
||||
await act(() => {
|
||||
ret.result.current[1]([100, 50, 50]);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(ret.result.current[0]).toStrictEqual(['50%', '25%', '25%']);
|
||||
});
|
||||
});
|
||||
});
|
47
libs/react-helpers/src/hooks/use-pane-layout.ts
Normal file
47
libs/react-helpers/src/hooks/use-pane-layout.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { useCallback } from 'react';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
|
||||
const STORAGE_KEY = 'vega_pane_store';
|
||||
const PANELS_SET_DEBOUNCE_TIME = 300;
|
||||
|
||||
export const usePaneLayoutStore = create<{
|
||||
sizes: Record<string, string[]>;
|
||||
valueSetter: (id: string, value: string[]) => void;
|
||||
}>()(
|
||||
persist(
|
||||
immer((set) => ({
|
||||
sizes: {},
|
||||
valueSetter: (id, value) =>
|
||||
set((state) => {
|
||||
state.sizes[id] = value;
|
||||
return state;
|
||||
}),
|
||||
})),
|
||||
{ name: STORAGE_KEY }
|
||||
)
|
||||
);
|
||||
|
||||
interface UsePaneLayoutProps {
|
||||
id: string;
|
||||
}
|
||||
export const usePaneLayout = ({
|
||||
id,
|
||||
}: UsePaneLayoutProps): [string[], (sizes: number[]) => void] => {
|
||||
const sizes = usePaneLayoutStore((store) => store.sizes[id]) || [];
|
||||
const valueSetter = usePaneLayoutStore((store) => store.valueSetter);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const handleOnChange = useCallback(
|
||||
debounce((args) => {
|
||||
if (args.length) {
|
||||
const all = args.reduce((agg: number, item: number) => agg + item, 0);
|
||||
const sizesArr = args.map((arg: number) => `${(arg / all) * 100}%`);
|
||||
valueSetter(id, sizesArr);
|
||||
}
|
||||
}, PANELS_SET_DEBOUNCE_TIME),
|
||||
[valueSetter, id]
|
||||
);
|
||||
return [sizes, handleOnChange];
|
||||
};
|
@ -120,7 +120,11 @@ describe('VegaConnectDialog', () => {
|
||||
.mockImplementation(() =>
|
||||
Promise.resolve({ success: true, error: null })
|
||||
);
|
||||
|
||||
jest
|
||||
.spyOn(connectors.rest, 'connect')
|
||||
.mockImplementation(() =>
|
||||
Promise.resolve([{ publicKey: 'pubkey', name: 'test key 1' }])
|
||||
);
|
||||
render(generateJSX());
|
||||
// Switches to rest form
|
||||
fireEvent.click(await screen.findByText('Hosted Fairground wallet'));
|
||||
@ -138,10 +142,11 @@ describe('VegaConnectDialog', () => {
|
||||
await act(async () => {
|
||||
fireEvent.submit(screen.getByTestId('rest-connector-form'));
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(spy).toHaveBeenCalledWith(fields);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(fields);
|
||||
|
||||
expect(mockCloseVegaDialog).toHaveBeenCalled();
|
||||
expect(mockCloseVegaDialog).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('handles failed connection', async () => {
|
||||
|
Loading…
Reference in New Issue
Block a user