From 7a99ded8e9e6fbdf2be44aaa3a50b6bccba9f745 Mon Sep 17 00:00:00 2001 From: Maciek Date: Wed, 3 May 2023 10:44:45 +0200 Subject: [PATCH] feat(trading): market view persistent pane sizes (#3537) --- .../client-pages/market/trade-grid.tsx | 45 +++++++++++------- .../client-pages/portfolio/portfolio.tsx | 7 +-- libs/react-helpers/src/hooks/index.ts | 1 + .../src/hooks/use-pane-layout.spec.ts | 19 ++++++++ .../src/hooks/use-pane-layout.ts | 47 +++++++++++++++++++ .../connect-dialog/connect-dialog.spec.tsx | 13 +++-- nx.json | 3 +- 7 files changed, 110 insertions(+), 25 deletions(-) create mode 100644 libs/react-helpers/src/hooks/use-pane-layout.spec.ts create mode 100644 libs/react-helpers/src/hooks/use-pane-layout.ts diff --git a/apps/trading/client-pages/market/trade-grid.tsx b/apps/trading/client-pages/market/trade-grid.tsx index ef31ef630..4026972e0 100644 --- a/apps/trading/client-pages/market/trade-grid.tsx +++ b/apps/trading/client-pages/market/trade-grid.tsx @@ -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 ? ( - + @@ -119,7 +127,7 @@ const MarketBottomPanel = memo( @@ -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 ( - + - + @@ -219,7 +234,7 @@ const MainGrid = memo( @@ -238,7 +253,7 @@ const MainGrid = memo( @@ -256,7 +271,7 @@ const MainGrid = memo( @@ -278,11 +293,7 @@ export const TradeGrid = ({ - + ); }; diff --git a/apps/trading/client-pages/portfolio/portfolio.tsx b/apps/trading/client-pages/portfolio/portfolio.tsx index b3b4a8dc9..5b8d26807 100644 --- a/apps/trading/client-pages/portfolio/portfolio.tsx +++ b/apps/trading/client-pages/portfolio/portfolio.tsx @@ -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 (
- + @@ -78,7 +79,7 @@ export const Portfolio = () => { diff --git a/libs/react-helpers/src/hooks/index.ts b/libs/react-helpers/src/hooks/index.ts index cc54cc063..92d319aa5 100644 --- a/libs/react-helpers/src/hooks/index.ts +++ b/libs/react-helpers/src/hooks/index.ts @@ -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'; diff --git a/libs/react-helpers/src/hooks/use-pane-layout.spec.ts b/libs/react-helpers/src/hooks/use-pane-layout.spec.ts new file mode 100644 index 000000000..8db8a27e1 --- /dev/null +++ b/libs/react-helpers/src/hooks/use-pane-layout.spec.ts @@ -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%']); + }); + }); +}); diff --git a/libs/react-helpers/src/hooks/use-pane-layout.ts b/libs/react-helpers/src/hooks/use-pane-layout.ts new file mode 100644 index 000000000..69832407e --- /dev/null +++ b/libs/react-helpers/src/hooks/use-pane-layout.ts @@ -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; + 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]; +}; diff --git a/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx b/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx index f4ca36a07..60aed606c 100644 --- a/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx +++ b/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx @@ -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 () => { diff --git a/nx.json b/nx.json index c0d4efa30..53753cc36 100644 --- a/nx.json +++ b/nx.json @@ -24,7 +24,8 @@ "echo $NX_TENDERMINT_WEBSOCKET_URL", "echo $NX_USE_ENV_OVERRIDES", "echo $NX_ETHEREUM_PROVIDER_URL" - ] + ], + "url": "https://cloud.nx.app" } } },