chore(trading): 4764 remember closed sidebar (#4799)

Co-authored-by: Madalina Raicu <madalina@raygroup.uk>
This commit is contained in:
Maciek 2023-09-15 14:36:08 +02:00 committed by GitHub
parent 78afecc2e5
commit 6d35f2b39d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 141 additions and 68 deletions

View File

@ -104,7 +104,7 @@ describe('deposit actions', { tags: '@smoke' }, () => {
cy.visit('/#/markets/market-1');
});
it('Deposit to trade is visible', () => {
it.skip('Deposit to trade is visible', () => {
cy.getByTestId('Collateral').click();
cy.get('[row-id="asset-id"]').contains('tEURO').should('be.visible');
cy.contains('[data-testid="deposit"]', 'Deposit').should('be.visible');

View File

@ -11,6 +11,7 @@ import { TradePanels } from './trade-panels';
import { useNavigate, useParams } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import { ViewType, useSidebar } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
return markPrice && decimalPlaces
@ -58,7 +59,9 @@ const TitleUpdater = ({
export const MarketPage = () => {
const { marketId } = useParams();
const navigate = useNavigate();
const { init, view, setView } = useSidebar();
const currentRouteId = useGetCurrentRouteId();
const { setViews, getView } = useSidebar();
const view = getView(currentRouteId);
const { screenSize } = useScreenDimensions();
const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize);
const update = useGlobalStore((store) => store.update);
@ -69,15 +72,14 @@ export const MarketPage = () => {
useEffect(() => {
if (data?.id && data.id !== lastMarketId) {
update({ marketId: data.id });
// make sidebar open on market id change
setView({ type: ViewType.Order });
}
}, [update, lastMarketId, data?.id]);
// make sidebar open on deal ticket by default
if (view === null) {
setView({ type: ViewType.Order });
useEffect(() => {
if (largeScreen && view === undefined) {
setViews({ type: ViewType.Order }, currentRouteId);
}
}, [update, lastMarketId, data?.id, setView, init, view]);
}, [setViews, view, currentRouteId, largeScreen]);
const tradeView = useMemo(() => {
if (largeScreen) {

View File

@ -23,6 +23,7 @@ import { ViewType, useSidebar } from '../../components/sidebar';
import { AccountsMenu } from '../../components/accounts-menu';
import { DepositsMenu } from '../../components/deposits-menu';
import { WithdrawalsMenu } from '../../components/withdrawals-menu';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
const WithdrawalsIndicator = () => {
const { ready } = useIncompleteWithdrawals();
@ -37,7 +38,10 @@ const WithdrawalsIndicator = () => {
};
export const Portfolio = () => {
const { init, view, setView } = useSidebar();
const currentRouteId = useGetCurrentRouteId();
const { getView, setViews } = useSidebar();
const view = getView(currentRouteId);
const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle,
}));
@ -48,10 +52,10 @@ export const Portfolio = () => {
// Make transfer sidebar open by default
useEffect(() => {
if (init && view === null) {
setView({ type: ViewType.Transfer });
if (view === undefined) {
setViews({ type: ViewType.Transfer }, currentRouteId);
}
}, [init, view, setView]);
}, [view, setViews, currentRouteId]);
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col';

View File

@ -12,6 +12,7 @@ import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
import { ViewType, useSidebar } from '../sidebar';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export const AccountsContainer = ({
pinnedAsset,
@ -21,7 +22,8 @@ export const AccountsContainer = ({
const onMarketClick = useMarketClickHandler(true);
const { pubKey, isReadOnly } = useVegaWallet();
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
const setView = useSidebar((store) => store.setView);
const currentRouteId = useGetCurrentRouteId();
const setViews = useSidebar((store) => store.setViews);
const gridStore = useAccountStore((store) => store.gridStore);
const updateGridStore = useAccountStore((store) => store.updateGridStore);
@ -49,13 +51,13 @@ export const AccountsContainer = ({
partyId={pubKey}
onClickAsset={onClickAsset}
onClickWithdraw={(assetId) => {
setView({ type: ViewType.Withdraw, assetId });
setViews({ type: ViewType.Withdraw, assetId }, currentRouteId);
}}
onClickDeposit={(assetId) => {
setView({ type: ViewType.Deposit, assetId });
setViews({ type: ViewType.Deposit, assetId }, currentRouteId);
}}
onClickTransfer={(assetId) => {
setView({ type: ViewType.Transfer, assetId });
setViews({ type: ViewType.Transfer, assetId }, currentRouteId);
}}
onMarketClick={onMarketClick}
isReadOnly={isReadOnly}

View File

@ -1,22 +1,24 @@
import { t } from '@vegaprotocol/i18n';
import { TradingButton } from '@vegaprotocol/ui-toolkit';
import { ViewType, useSidebar } from '../sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export const AccountsMenu = () => {
const setView = useSidebar((store) => store.setView);
const currentRouteId = useGetCurrentRouteId();
const setViews = useSidebar((store) => store.setViews);
return (
<>
<TradingButton
size="extra-small"
data-testid="open-transfer"
onClick={() => setView({ type: ViewType.Transfer })}
onClick={() => setViews({ type: ViewType.Transfer }, currentRouteId)}
>
{t('Transfer')}
</TradingButton>
<TradingButton
size="extra-small"
onClick={() => setView({ type: ViewType.Deposit })}
onClick={() => setViews({ type: ViewType.Deposit }, currentRouteId)}
>
{t('Deposit')}
</TradingButton>

View File

@ -1,14 +1,16 @@
import { t } from '@vegaprotocol/i18n';
import { TradingButton } from '@vegaprotocol/ui-toolkit';
import { ViewType, useSidebar } from '../sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export const DepositsMenu = () => {
const setView = useSidebar((store) => store.setView);
const currentRouteId = useGetCurrentRouteId();
const setViews = useSidebar((store) => store.setViews);
return (
<TradingButton
size="extra-small"
onClick={() => setView({ type: ViewType.Deposit })}
onClick={() => setViews({ type: ViewType.Deposit }, currentRouteId)}
data-testid="deposit-button"
>
{t('Deposit')}

View File

@ -4,11 +4,13 @@ import classNames from 'classnames';
import { Routes as AppRoutes } from '../../pages/client-router';
import { MarketHeader } from '../market-header';
import { LiquidityHeader } from '../liquidity-header';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export const LayoutWithSidebar = () => {
const sidebarView = useSidebar((store) => store.view);
const currentRouteId = useGetCurrentRouteId();
const views = useSidebar((store) => store.views);
const sidebarView = views[currentRouteId] || null;
const sidebarOpen = sidebarView !== null;
const gridClasses = classNames(
'h-full relative z-0 grid',
'grid-rows-[min-content_1fr_40px]',

View File

@ -1,16 +1,18 @@
import { OrderbookManager } from '@vegaprotocol/market-depth';
import { ViewType, useSidebar } from '../sidebar';
import { useDealTicketFormValues } from '@vegaprotocol/deal-ticket';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export const OrderbookContainer = ({ marketId }: { marketId: string }) => {
const currentRouteId = useGetCurrentRouteId();
const update = useDealTicketFormValues((state) => state.updateAll);
const setView = useSidebar((store) => store.setView);
const setViews = useSidebar((store) => store.setViews);
return (
<OrderbookManager
marketId={marketId}
onClick={(values) => {
update(marketId, values);
setView({ type: ViewType.Order });
setViews({ type: ViewType.Order }, currentRouteId);
}}
/>
);

View File

@ -11,6 +11,7 @@ import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { Routes as AppRoutes } from '../../pages/client-router';
jest.mock('../node-health', () => ({
NodeHealthContainer: () => <span data-testid="node-health" />,
@ -115,7 +116,11 @@ describe('SidebarContent', () => {
<VegaWalletContext.Provider value={walletContext}>
<MemoryRouter initialEntries={['/markets/ABC']}>
<Routes>
<Route path="/markets/:marketId" element={<SidebarContent />} />
<Route
path="/markets/:marketId"
id={AppRoutes.MARKET}
element={<SidebarContent />}
/>
</Routes>
</MemoryRouter>
</VegaWalletContext.Provider>
@ -124,13 +129,17 @@ describe('SidebarContent', () => {
expect(container).toBeEmptyDOMElement();
act(() => {
useSidebar.setState({ view: { type: ViewType.Transfer } });
useSidebar.setState({
views: { [AppRoutes.MARKET]: { type: ViewType.Transfer } },
});
});
expect(screen.getByTestId('transfer')).toBeInTheDocument();
act(() => {
useSidebar.setState({ view: { type: ViewType.Deposit } });
useSidebar.setState({
views: { [AppRoutes.MARKET]: { type: ViewType.Deposit } },
});
});
expect(screen.getByTestId('deposit')).toBeInTheDocument();
@ -141,26 +150,36 @@ describe('SidebarContent', () => {
<VegaWalletContext.Provider value={walletContext}>
<MemoryRouter initialEntries={['/portfolio']}>
<Routes>
<Route path="/portfolio" element={<SidebarContent />} />
<Route
path="/portfolio"
id={AppRoutes.PORTFOLIO}
element={<SidebarContent />}
/>
</Routes>
</MemoryRouter>
</VegaWalletContext.Provider>
);
act(() => {
useSidebar.setState({ view: { type: ViewType.Order } });
useSidebar.setState({
views: { [AppRoutes.PORTFOLIO]: { type: ViewType.Order } },
});
});
expect(container).toBeEmptyDOMElement();
act(() => {
useSidebar.setState({ view: { type: ViewType.Settings } });
useSidebar.setState({
views: { [AppRoutes.PORTFOLIO]: { type: ViewType.Settings } },
});
});
expect(screen.getByTestId('settings')).toBeInTheDocument();
act(() => {
useSidebar.setState({ view: { type: ViewType.Info } });
useSidebar.setState({
views: { [AppRoutes.PORTFOLIO]: { type: ViewType.Info } },
});
});
expect(container).toBeEmptyDOMElement();
@ -178,6 +197,7 @@ describe('SidebarButton', () => {
tooltip="INFO"
onClick={onClick}
view={view}
routeId="current-route-id"
/>
);

View File

@ -16,6 +16,7 @@ import { WithdrawContainer } from '../withdraw-container';
import { Routes as AppRoutes } from '../../pages/client-router';
import { GetStarted } from '../welcome-dialog';
import { useVegaWallet, useViewAsDialog } from '@vegaprotocol/wallet';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export enum ViewType {
Order = 'Order',
@ -51,6 +52,7 @@ type SidebarView =
};
export const Sidebar = () => {
const currentRouteId = useGetCurrentRouteId();
const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1';
const setViewAsDialogOpen = useViewAsDialog((state) => state.setOpen);
const { pubKeys } = useVegaWallet();
@ -62,16 +64,19 @@ export const Sidebar = () => {
view={ViewType.Deposit}
icon={VegaIconNames.DEPOSIT}
tooltip={t('Deposit')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Withdraw}
icon={VegaIconNames.WITHDRAW}
tooltip={t('Withdraw')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Transfer}
icon={VegaIconNames.TRANSFER}
tooltip={t('Transfer')}
routeId={currentRouteId}
/>
{/* buttons for specific routes */}
<Routes>
@ -94,11 +99,13 @@ export const Sidebar = () => {
view={ViewType.Order}
icon={VegaIconNames.TICKET}
tooltip={t('Order')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Info}
icon={VegaIconNames.BREAKDOWN}
tooltip={t('Market specification')}
routeId={currentRouteId}
/>
</>
}
@ -114,12 +121,13 @@ export const Sidebar = () => {
icon={VegaIconNames.EYE}
tooltip={t('View as party')}
disabled={Boolean(pubKeys)}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Settings}
icon={VegaIconNames.COG}
tooltip={t('Settings')}
routeId={currentRouteId}
/>
<NodeHealthContainer />
</nav>
@ -133,23 +141,25 @@ export const SidebarButton = ({
tooltip,
disabled = false,
onClick,
routeId,
}: {
view?: ViewType;
icon: VegaIconNames;
tooltip: string;
disabled?: boolean;
onClick?: () => void;
routeId: string;
}) => {
const { currView, setView } = useSidebar((store) => ({
currView: store.view,
setView: store.setView,
const { setViews, getView } = useSidebar((store) => ({
setViews: store.setViews,
getView: store.getView,
}));
const currView = getView(routeId);
const onSelect = (view: SidebarView['type']) => {
if (view === currView?.type) {
setView(null);
setViews(null, routeId);
} else {
setView({ type: view });
setViews({ type: view }, routeId);
}
};
@ -195,8 +205,10 @@ const SidebarDivider = () => {
export const SidebarContent = () => {
const params = useParams();
const { view, setView } = useSidebar();
const currentRouteId = useGetCurrentRouteId();
const { setViews, getView } = useSidebar();
const view = getView(currentRouteId);
if (!view) return null;
if (view.type === ViewType.Order) {
@ -206,7 +218,7 @@ export const SidebarContent = () => {
<DealTicketContainer
marketId={params.marketId}
onDeposit={(assetId) =>
setView({ type: ViewType.Deposit, assetId })
setViews({ type: ViewType.Deposit, assetId }, currentRouteId)
}
/>
<GetStarted />
@ -288,25 +300,21 @@ const ContentWrapper = ({
/** If rendered will close sidebar */
const CloseSidebar = () => {
const setView = useSidebar((store) => store.setView);
const currentRouteId = useGetCurrentRouteId();
const setViews = useSidebar((store) => store.setViews);
useEffect(() => {
setView(null);
}, [setView]);
setViews(null, currentRouteId);
}, [setViews, currentRouteId]);
return null;
};
export const useSidebar = create<{
init: boolean;
view: SidebarView | null;
setView: (view: SidebarView | null) => void;
}>()((set) => ({
init: true,
view: null,
setView: (x) =>
set(() => {
if (x == null) {
return { view: null, init: false };
}
return { view: x, init: false };
}),
views: { [key: string]: SidebarView | null };
setViews: (view: SidebarView | null, routeId: string) => void;
getView: (routeId: string) => SidebarView | null | undefined;
}>()((set, get) => ({
views: {},
setViews: (x, routeId) =>
set(({ views }) => ({ views: { ...views, [routeId]: x } })),
getView: (routeId) => get().views[routeId],
}));

View File

@ -11,6 +11,10 @@ jest.mock('@vegaprotocol/wallet', () => ({
useVegaWalletDialogStore: () => mockUpdateDialogOpen,
}));
jest.mock('../../lib/hooks/use-get-current-route-id', () => ({
useGetCurrentRouteId: jest.fn().mockReturnValue('current-route-id'),
}));
beforeEach(() => {
jest.clearAllMocks();
});

View File

@ -22,13 +22,15 @@ import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { useCopyTimeout } from '@vegaprotocol/react-helpers';
import { ViewType, useSidebar } from '../sidebar';
import classNames from 'classnames';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export const VegaWalletConnectButton = () => {
const [dropdownOpen, setDropdownOpen] = useState(false);
const openVegaWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
const setView = useSidebar((store) => store.setView);
const currentRouteId = useGetCurrentRouteId();
const setViews = useSidebar((store) => store.setViews);
const {
pubKey,
pubKeys,
@ -95,7 +97,7 @@ export const VegaWalletConnectButton = () => {
<TradingDropdownItem
data-testid="wallet-transfer"
onClick={() => {
setView({ type: ViewType.Transfer });
setViews({ type: ViewType.Transfer }, currentRouteId);
setDropdownOpen(false);
}}
>

View File

@ -10,6 +10,7 @@ import { useVegaWallet, type PubKey } from '@vegaprotocol/wallet';
import { useCallback, useMemo } from 'react';
import CopyToClipboard from 'react-copy-to-clipboard';
import { ViewType, useSidebar } from '../sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export const VegaWalletMenu = ({
setMenu,
@ -17,7 +18,8 @@ export const VegaWalletMenu = ({
setMenu: (open: 'nav' | 'wallet' | null) => void;
}) => {
const { pubKey, pubKeys, selectPubKey, disconnect } = useVegaWallet();
const setView = useSidebar((store) => store.setView);
const currentRouteId = useGetCurrentRouteId();
const setViews = useSidebar((store) => store.setViews);
const activeKey = useMemo(() => {
return pubKeys?.find((pk) => pk.publicKey === pubKey);
@ -46,7 +48,7 @@ export const VegaWalletMenu = ({
<div className="flex flex-col gap-2 m-4">
<Button
onClick={() => {
setView({ type: ViewType.Transfer });
setViews({ type: ViewType.Transfer }, currentRouteId);
setMenu(null);
}}
>

View File

@ -32,7 +32,7 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
const openVegaWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
const setView = useSidebar((store) => store.setView);
const setViews = useSidebar((store) => store.setViews);
let buttonText = t('Get started');
let onClickHandle = () => {
openVegaWalletDialog();
@ -43,14 +43,14 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
buttonText = t('Deposit');
onClickHandle = () => {
navigate(link);
setView({ type: ViewType.Deposit });
setViews({ type: ViewType.Deposit }, Routes.MARKET);
setDialogOpen(false);
};
} else if (step >= OnboardingStep.ONBOARDING_ORDER_STEP) {
buttonText = t('Ready to trade');
onClickHandle = () => {
navigate(link);
setView({ type: ViewType.Order });
setViews({ type: ViewType.Order }, Routes.MARKET);
dismiss();
};
}

View File

@ -1,14 +1,15 @@
import { t } from '@vegaprotocol/i18n';
import { TradingButton } from '@vegaprotocol/ui-toolkit';
import { ViewType, useSidebar } from '../sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
export const WithdrawalsMenu = () => {
const setView = useSidebar((store) => store.setView);
const setViews = useSidebar((store) => store.setViews);
const currentRouteId = useGetCurrentRouteId();
return (
<TradingButton
size="extra-small"
onClick={() => setView({ type: ViewType.Withdraw })}
onClick={() => setViews({ type: ViewType.Withdraw }, currentRouteId)}
data-testid="withdraw-dialog-button"
>
{t('Make withdrawal')}

View File

@ -0,0 +1,15 @@
import { routerConfig } from '../../pages/client-router';
import { matchRoutes, useLocation } from 'react-router-dom';
export const useGetCurrentRouteId = () => {
const location = useLocation();
const currentRoute = matchRoutes(routerConfig, location);
const lastRoute = currentRoute?.pop();
if (lastRoute) {
const {
route: { id },
} = lastRoute;
return id || '';
}
return '';
};

View File

@ -59,7 +59,7 @@ export const Links: ConsoleLinks = {
[Routes.DEPOSIT]: () => Routes.DEPOSIT,
};
const routerConfig: RouteObject[] = [
export const routerConfig: RouteObject[] = [
{
path: '/*',
element: <LayoutWithSidebar />,
@ -68,6 +68,7 @@ const routerConfig: RouteObject[] = [
{
index: true,
element: <LazyHome />,
id: Routes.HOME,
},
{
path: 'markets',
@ -76,16 +77,19 @@ const routerConfig: RouteObject[] = [
{
path: 'all',
element: <LazyMarkets />,
id: Routes.MARKETS,
},
{
path: ':marketId',
element: <LazyMarket />,
id: Routes.MARKET,
},
],
},
{
path: 'portfolio',
element: <LazyPortfolio />,
id: Routes.PORTFOLIO,
},
{
path: 'liquidity',
@ -94,6 +98,7 @@ const routerConfig: RouteObject[] = [
{
path: ':marketId',
element: <LazyLiquidity />,
id: Routes.LIQUIDITY,
},
],
},