feat: add a global zustand store for managing dialogs (#494)
* feat: add a global zustand store for managing connect dialogs and landing dialog * feat: add tests * fix: remove condition for cypress for auto connecting * chore: fix assertion in tests for vega wallet text * fix: add mock for landing dialog markets query Co-authored-by: madalinaraicu <madalina@vegaprotocol.io>
This commit is contained in:
parent
e63e17f173
commit
25b67009a6
77
apps/trading-e2e/src/support/mocks/generate-market-list.ts
Normal file
77
apps/trading-e2e/src/support/mocks/generate-market-list.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import merge from 'lodash/merge';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type { MarketList, MarketList_markets } from '@vegaprotocol/market-list';
|
||||
|
||||
export const generateMarketList = (
|
||||
override?: PartialDeep<MarketList>
|
||||
): MarketList => {
|
||||
const markets: MarketList_markets[] = [
|
||||
{
|
||||
id: 'market-id',
|
||||
decimalPlaces: 5,
|
||||
data: {
|
||||
market: {
|
||||
id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35',
|
||||
__typename: 'Market',
|
||||
},
|
||||
markPrice: '4612690058',
|
||||
__typename: 'MarketData',
|
||||
},
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: 'BTC/USD Monthly',
|
||||
code: 'BTCUSD.MF21',
|
||||
metadata: {
|
||||
__typename: 'InstrumentMetadata',
|
||||
tags: ['tag1'],
|
||||
},
|
||||
__typename: 'Instrument',
|
||||
},
|
||||
__typename: 'TradableInstrument',
|
||||
},
|
||||
marketTimestamps: {
|
||||
__typename: 'MarketTimestamps',
|
||||
open: '',
|
||||
close: '',
|
||||
},
|
||||
candles: [{ __typename: 'Candle', open: '100', close: '100' }],
|
||||
__typename: 'Market',
|
||||
},
|
||||
{
|
||||
id: 'test-market-suspended',
|
||||
decimalPlaces: 2,
|
||||
data: {
|
||||
market: {
|
||||
id: '34d95e10faa00c21d19d382d6d7e6fc9722a96985369f0caec041b0f44b775ed',
|
||||
__typename: 'Market',
|
||||
},
|
||||
markPrice: '8441',
|
||||
__typename: 'MarketData',
|
||||
},
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: 'SOL/USD',
|
||||
code: 'SOLUSD',
|
||||
metadata: {
|
||||
__typename: 'InstrumentMetadata',
|
||||
tags: ['tag1'],
|
||||
},
|
||||
__typename: 'Instrument',
|
||||
},
|
||||
__typename: 'TradableInstrument',
|
||||
},
|
||||
marketTimestamps: {
|
||||
__typename: 'MarketTimestamps',
|
||||
open: '',
|
||||
close: '',
|
||||
},
|
||||
candles: [{ __typename: 'Candle', open: '100', close: '100' }],
|
||||
__typename: 'Market',
|
||||
},
|
||||
];
|
||||
const defaultResult = {
|
||||
markets,
|
||||
};
|
||||
|
||||
return merge(defaultResult, override);
|
||||
};
|
@ -31,7 +31,7 @@ export default class WithdrawalsPage extends BasePage {
|
||||
validateConnectWalletText() {
|
||||
cy.getByTestId(this.connectVegaWalletText).should(
|
||||
'have.text',
|
||||
'Please connect your Vega wallet'
|
||||
'Connect your Vega wallet'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,18 @@
|
||||
import { Given } from 'cypress-cucumber-preprocessor/steps';
|
||||
import { hasOperationName } from '..';
|
||||
import { generateMarketList } from '../mocks/generate-market-list';
|
||||
import BasePage from '../pages/base-page';
|
||||
|
||||
const basePage = new BasePage();
|
||||
|
||||
Given('I am on the homepage', () => {
|
||||
cy.mockGQL('MarketsList', (req) => {
|
||||
if (hasOperationName(req, 'MarketsList')) {
|
||||
req.reply({
|
||||
body: { data: generateMarketList() },
|
||||
});
|
||||
}
|
||||
});
|
||||
cy.visit('/');
|
||||
basePage.closeDialog();
|
||||
});
|
||||
|
1
apps/trading/components/vega-wallet-container/index.ts
Normal file
1
apps/trading/components/vega-wallet-container/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './vega-wallet-container';
|
@ -0,0 +1,27 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { VegaWalletContainer } from './vega-wallet-container';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
|
||||
const generateJsx = (context: PartialDeep<VegaWalletContextShape>) => {
|
||||
return (
|
||||
<VegaWalletContext.Provider value={context as VegaWalletContextShape}>
|
||||
<VegaWalletContainer>
|
||||
<div data-testid="child" />
|
||||
</VegaWalletContainer>
|
||||
</VegaWalletContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('VegaWalletContainer', () => {
|
||||
it('doesnt render children if not connected', () => {
|
||||
render(generateJsx({ keypair: null }));
|
||||
expect(screen.queryByTestId('child')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders children if connected', () => {
|
||||
render(generateJsx({ keypair: { pub: '0x123' } }));
|
||||
expect(screen.getByTestId('child')).toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -0,0 +1,34 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
interface VegaWalletContainerProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
|
||||
const store = useGlobalStore();
|
||||
const { keypair } = useVegaWallet();
|
||||
|
||||
if (!keypair) {
|
||||
return (
|
||||
<Splash>
|
||||
<div className="text-center">
|
||||
<p className="mb-12" data-testid="connect-vega-wallet-text">
|
||||
{t('Connect your Vega wallet')}
|
||||
</p>
|
||||
<Button
|
||||
onClick={() => store.setVegaWalletConnectDialog(true)}
|
||||
data-testid="vega-wallet-connect"
|
||||
>
|
||||
{t('Connect')}
|
||||
</Button>
|
||||
</div>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
@ -108,11 +108,7 @@ export const Web3Content = ({
|
||||
const { isActive, error, connector, chainId } = useWeb3React();
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
connector?.connectEagerly &&
|
||||
// Dont eager connect if this is a cypress test run
|
||||
'Cypress' in window
|
||||
) {
|
||||
if (connector?.connectEagerly) {
|
||||
connector.connectEagerly();
|
||||
}
|
||||
}, [connector]);
|
||||
|
@ -9,20 +9,18 @@ import {
|
||||
} from '@vegaprotocol/wallet';
|
||||
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
|
||||
import { Connectors } from '../lib/vega-connectors';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { createClient } from '../lib/apollo-client';
|
||||
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
||||
import { ApolloProvider } from '@apollo/client';
|
||||
import { AppLoader } from '../components/app-loader';
|
||||
import { VegaWalletConnectButton } from '../components/vega-wallet-connect-button';
|
||||
import './styles.css';
|
||||
import { useGlobalStore } from '../stores';
|
||||
|
||||
function VegaTradingApp({ Component, pageProps }: AppProps) {
|
||||
const client = useMemo(() => createClient(process.env['NX_VEGA_URL']), []);
|
||||
const [vegaWallet, setVegaWallet] = useState({
|
||||
connect: false,
|
||||
manage: false,
|
||||
});
|
||||
const store = useGlobalStore();
|
||||
const [theme, toggleTheme] = useThemeSwitcher();
|
||||
|
||||
return (
|
||||
@ -55,12 +53,12 @@ function VegaTradingApp({ Component, pageProps }: AppProps) {
|
||||
<Navbar />
|
||||
<div className="flex items-center gap-4 ml-auto mr-8">
|
||||
<VegaWalletConnectButton
|
||||
setConnectDialog={(open) =>
|
||||
setVegaWallet((x) => ({ ...x, connect: open }))
|
||||
}
|
||||
setManageDialog={(open) =>
|
||||
setVegaWallet((x) => ({ ...x, manage: open }))
|
||||
}
|
||||
setConnectDialog={(open) => {
|
||||
store.setVegaWalletConnectDialog(open);
|
||||
}}
|
||||
setManageDialog={(open) => {
|
||||
store.setVegaWalletManageDialog(open);
|
||||
}}
|
||||
/>
|
||||
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" />
|
||||
</div>
|
||||
@ -71,15 +69,15 @@ function VegaTradingApp({ Component, pageProps }: AppProps) {
|
||||
</main>
|
||||
<VegaConnectDialog
|
||||
connectors={Connectors}
|
||||
dialogOpen={vegaWallet.connect}
|
||||
dialogOpen={store.vegaWalletConnectDialog}
|
||||
setDialogOpen={(open) =>
|
||||
setVegaWallet((x) => ({ ...x, connect: open }))
|
||||
store.setVegaWalletConnectDialog(open)
|
||||
}
|
||||
/>
|
||||
<VegaManageDialog
|
||||
dialogOpen={vegaWallet.manage}
|
||||
dialogOpen={store.vegaWalletManageDialog}
|
||||
setDialogOpen={(open) =>
|
||||
setVegaWallet((x) => ({ ...x, manage: open }))
|
||||
store.setVegaWalletManageDialog(open)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import { LandingDialog } from '@vegaprotocol/market-list';
|
||||
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import MarketPage from './markets/[marketId].page';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect } from 'react';
|
||||
import { useGlobalStore } from '../stores';
|
||||
import type { MarketsLanding } from './__generated__/MarketsLanding';
|
||||
|
||||
const MARKETS_QUERY = gql`
|
||||
@ -29,24 +30,33 @@ const marketList = ({ markets }: MarketsLanding) =>
|
||||
);
|
||||
|
||||
export function Index() {
|
||||
const { replace } = useRouter();
|
||||
// The default market selected in the platform behind the overlay
|
||||
// should be the oldest market that is currently trading in continuous mode(i.e. not in auction).
|
||||
const { data, error, loading } = useQuery<MarketsLanding>(MARKETS_QUERY);
|
||||
if (data && !error && !loading) {
|
||||
const marketId = marketList(data)[0]?.id;
|
||||
window.history.replaceState(
|
||||
data,
|
||||
'',
|
||||
marketId ? `/markets/${marketId}` : '/markets'
|
||||
);
|
||||
}
|
||||
const setLandingDialog = useGlobalStore((state) => state.setLandingDialog);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
const marketId = marketList(data)[0]?.id;
|
||||
|
||||
// If a default market is found, go to it with the landing dialog open
|
||||
if (marketId) {
|
||||
setLandingDialog(true);
|
||||
replace(`/markets/${marketId}`);
|
||||
}
|
||||
// Fallback to the markets list page
|
||||
else {
|
||||
replace('/markets');
|
||||
}
|
||||
}
|
||||
}, [data, replace, setLandingDialog]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<LandingDialog />
|
||||
<AsyncRenderer data={data} error={error} loading={loading}>
|
||||
<MarketPage id={data && marketList(data)[0]?.id} />
|
||||
</AsyncRenderer>
|
||||
</>
|
||||
<AsyncRenderer data={data} loading={loading} error={error}>
|
||||
{/* Render a loading and error state but we will redirect if markets are found */}
|
||||
{null}
|
||||
</AsyncRenderer>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@ import debounce from 'lodash/debounce';
|
||||
import { PageQueryContainer } from '../../components/page-query-container';
|
||||
import { TradeGrid, TradePanels } from './trade-grid';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { LandingDialog } from '@vegaprotocol/market-list';
|
||||
|
||||
// Top level page query
|
||||
const MARKET_QUERY = gql`
|
||||
@ -21,6 +23,7 @@ const MARKET_QUERY = gql`
|
||||
const MarketPage = ({ id }: { id?: string }) => {
|
||||
const { query } = useRouter();
|
||||
const { w } = useWindowSize();
|
||||
const store = useGlobalStore();
|
||||
|
||||
// Default to first marketId query item if found
|
||||
const marketId =
|
||||
@ -48,10 +51,18 @@ const MarketPage = ({ id }: { id?: string }) => {
|
||||
return <Splash>{t('Market not found')}</Splash>;
|
||||
}
|
||||
|
||||
return w > 960 ? (
|
||||
<TradeGrid market={market} />
|
||||
) : (
|
||||
<TradePanels market={market} />
|
||||
return (
|
||||
<>
|
||||
{w > 960 ? (
|
||||
<TradeGrid market={market} />
|
||||
) : (
|
||||
<TradePanels market={market} />
|
||||
)}
|
||||
<LandingDialog
|
||||
open={store.landingDialog}
|
||||
setOpen={(isOpen) => store.setLandingDialog(isOpen)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Web3Container } from '../../../components/web3-container';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useMemo } from 'react';
|
||||
import { WithdrawPageContainer } from './withdraw-page-container';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { VegaWalletContainer } from '../../../components/vega-wallet-container';
|
||||
import { Web3Container } from '../../../components/web3-container';
|
||||
|
||||
const Withdraw = () => {
|
||||
const { query } = useRouter();
|
||||
@ -21,14 +22,16 @@ const Withdraw = () => {
|
||||
}, [query]);
|
||||
|
||||
return (
|
||||
<Web3Container
|
||||
render={() => (
|
||||
<div className="max-w-[420px] p-24 mx-auto">
|
||||
<h1 className="text-h3 mb-12">{t('Withdraw')}</h1>
|
||||
<WithdrawPageContainer assetId={assetId} />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<VegaWalletContainer>
|
||||
<Web3Container
|
||||
render={() => (
|
||||
<div className="max-w-[420px] p-24 mx-auto">
|
||||
<h1 className="text-h3 mb-12">{t('Withdraw')}</h1>
|
||||
<WithdrawPageContainer assetId={assetId} />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -47,14 +47,6 @@ export const WithdrawPageContainer = ({
|
||||
}: WithdrawPageContainerProps) => {
|
||||
const { keypair } = useVegaWallet();
|
||||
|
||||
if (!keypair) {
|
||||
return (
|
||||
<p data-testid="connect-vega-wallet-text">
|
||||
{t('Please connect your Vega wallet')}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PageQueryContainer<WithdrawPageQuery, WithdrawPageQueryVariables>
|
||||
query={WITHDRAW_PAGE_QUERY}
|
||||
|
@ -1,30 +1,26 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { AnchorButton, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { AnchorButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { VegaWalletContainer } from '../../../components/vega-wallet-container';
|
||||
import { Web3Container } from '../../../components/web3-container';
|
||||
import { WithdrawalsPageContainer } from './withdrawals-page-container';
|
||||
|
||||
const Withdrawals = () => {
|
||||
const { keypair } = useVegaWallet();
|
||||
|
||||
if (!keypair) {
|
||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Web3Container
|
||||
render={() => (
|
||||
<div className="h-full grid grid grid-rows-[min-content,1fr]">
|
||||
<header className="flex justify-between p-24">
|
||||
<h1 className="text-h3">{t('Withdrawals')}</h1>
|
||||
<AnchorButton href="/portfolio/withdraw">
|
||||
{t('Start withdrawal')}
|
||||
</AnchorButton>
|
||||
</header>
|
||||
<WithdrawalsPageContainer />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<VegaWalletContainer>
|
||||
<Web3Container
|
||||
render={() => (
|
||||
<div className="h-full grid grid grid-rows-[min-content,1fr]">
|
||||
<header className="flex justify-between p-24">
|
||||
<h1 className="text-h3">{t('Withdrawals')}</h1>
|
||||
<AnchorButton href="/portfolio/withdraw">
|
||||
{t('Start withdrawal')}
|
||||
</AnchorButton>
|
||||
</header>
|
||||
<WithdrawalsPageContainer />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
26
apps/trading/stores/global.ts
Normal file
26
apps/trading/stores/global.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import type { SetState } from 'zustand';
|
||||
import create from 'zustand';
|
||||
|
||||
interface GlobalStore {
|
||||
vegaWalletConnectDialog: boolean;
|
||||
setVegaWalletConnectDialog: (isOpen: boolean) => void;
|
||||
vegaWalletManageDialog: boolean;
|
||||
setVegaWalletManageDialog: (isOpen: boolean) => void;
|
||||
landingDialog: boolean;
|
||||
setLandingDialog: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
export const useGlobalStore = create((set: SetState<GlobalStore>) => ({
|
||||
vegaWalletConnectDialog: false,
|
||||
setVegaWalletConnectDialog: (isOpen: boolean) => {
|
||||
set({ vegaWalletConnectDialog: isOpen });
|
||||
},
|
||||
vegaWalletManageDialog: false,
|
||||
setVegaWalletManageDialog: (isOpen: boolean) => {
|
||||
set({ vegaWalletManageDialog: isOpen });
|
||||
},
|
||||
landingDialog: false,
|
||||
setLandingDialog: (isOpen: boolean) => {
|
||||
set({ landingDialog: isOpen });
|
||||
},
|
||||
}));
|
1
apps/trading/stores/index.ts
Normal file
1
apps/trading/stores/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './global';
|
@ -2,14 +2,17 @@ import { useQuery } from '@apollo/client';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Interval } from '@vegaprotocol/types';
|
||||
import { AsyncRenderer, Dialog, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { useState } from 'react';
|
||||
import { MARKET_LIST_QUERY } from '../markets-container/markets-data-provider';
|
||||
import type { MarketList } from '../markets-container/__generated__/MarketList';
|
||||
|
||||
import { SelectMarketList } from './select-market-list';
|
||||
|
||||
export const LandingDialog = () => {
|
||||
const [open, setOpen] = useState(true);
|
||||
interface LandingDialogProps {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export const LandingDialog = ({ open, setOpen }: LandingDialogProps) => {
|
||||
const setClose = () => setOpen(false);
|
||||
|
||||
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||
@ -21,17 +24,15 @@ export const LandingDialog = () => {
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||
{
|
||||
<Dialog
|
||||
title={t('Select a market to get started')}
|
||||
intent={Intent.Prompt}
|
||||
open={open}
|
||||
onChange={setClose}
|
||||
titleClassNames="font-bold font-sans text-3xl tracking-tight mb-0 pl-8"
|
||||
>
|
||||
<SelectMarketList data={data} />
|
||||
</Dialog>
|
||||
}
|
||||
<Dialog
|
||||
title={t('Select a market to get started')}
|
||||
intent={Intent.Prompt}
|
||||
open={open}
|
||||
onChange={setClose}
|
||||
titleClassNames="font-bold font-sans text-3xl tracking-tight mb-0 pl-8"
|
||||
>
|
||||
<SelectMarketList data={data} onSelect={setClose} />
|
||||
</Dialog>
|
||||
</AsyncRenderer>
|
||||
);
|
||||
};
|
||||
|
@ -1,15 +1,41 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import type { MarketList } from '../__generated__/MarketList';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { MarketList } from '../markets-container/__generated__/MarketList';
|
||||
import { SelectMarketList } from './select-market-list';
|
||||
|
||||
jest.mock(
|
||||
'next/link',
|
||||
() =>
|
||||
({ children }: { children: ReactNode }) =>
|
||||
children
|
||||
);
|
||||
|
||||
describe('SelectMarketList', () => {
|
||||
it('should render', () => {
|
||||
render(<SelectMarketList data={mockData.data as MarketList} />);
|
||||
render(
|
||||
<SelectMarketList
|
||||
data={mockData.data as MarketList}
|
||||
onSelect={jest.fn()}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText('AAPL.MF21')).toBeTruthy();
|
||||
expect(screen.getByText('-3.14%')).toBeTruthy();
|
||||
expect(screen.getByText('141.75')).toBeTruthy();
|
||||
expect(screen.getByText('Or view full market list')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should call onSelect callback', () => {
|
||||
const onSelect = jest.fn();
|
||||
const expectedMarket = mockData.data.markets[0];
|
||||
render(
|
||||
<SelectMarketList
|
||||
data={mockData.data as MarketList}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
);
|
||||
fireEvent.click(screen.getByTestId(`market-link-${expectedMarket.id}`));
|
||||
expect(onSelect).toHaveBeenCalledWith(expectedMarket.id);
|
||||
});
|
||||
});
|
||||
|
||||
const mockData = {
|
||||
|
@ -10,11 +10,12 @@ import type { MarketList } from '../markets-container/__generated__/MarketList';
|
||||
|
||||
export interface SelectMarketListProps {
|
||||
data: MarketList | undefined;
|
||||
onSelect: (id: string) => void;
|
||||
}
|
||||
|
||||
type CandleClose = Required<string>;
|
||||
|
||||
export const SelectMarketList = ({ data }: SelectMarketListProps) => {
|
||||
export const SelectMarketList = ({ data, onSelect }: SelectMarketListProps) => {
|
||||
const thClassNames = (direction: 'left' | 'right') =>
|
||||
`px-8 text-${direction} font-sans font-normal text-ui-small leading-9 mb-0 text-dark/80 dark:text-white/80`;
|
||||
const tdClassNames =
|
||||
@ -49,8 +50,15 @@ export const SelectMarketList = ({ data }: SelectMarketListProps) => {
|
||||
<td className={`${boldUnderlineClassNames} relative`}>
|
||||
<Link
|
||||
href={`/markets/${id}?portfolio=orders&trade=orderbook&chart=candles`}
|
||||
passHref={true}
|
||||
>
|
||||
{marketName}
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
||||
<a
|
||||
onClick={() => onSelect(id)}
|
||||
data-testid={`market-link-${id}`}
|
||||
>
|
||||
{marketName}
|
||||
</a>
|
||||
</Link>
|
||||
</td>
|
||||
<td className={tdClassNames}>
|
||||
|
@ -2,3 +2,4 @@ export * from './market-list-table';
|
||||
export * from './markets-container';
|
||||
export * from './markets-data-provider';
|
||||
export * from './summary-cell';
|
||||
export * from './__generated__/MarketList';
|
||||
|
@ -71,7 +71,8 @@
|
||||
"tailwindcss": "^3.0.23",
|
||||
"tslib": "^2.0.0",
|
||||
"uuid": "^8.3.2",
|
||||
"web-vitals": "^2.1.4"
|
||||
"web-vitals": "^2.1.4",
|
||||
"zustand": "^4.0.0-rc.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apollo/react-testing": "^4.0.0",
|
||||
|
12
yarn.lock
12
yarn.lock
@ -21214,6 +21214,11 @@ use-sync-external-store@1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.0.0.tgz#d98f4a9c2e73d0f958e7e2d2c2bfb5f618cbd8fd"
|
||||
integrity sha512-AFVsxg5GkFg8GDcxnl+Z0lMAz9rE8DGJCc28qnBuQF7lac57B5smLcT37aXpXIIPz75rW4g3eXHPjhHwdGskOw==
|
||||
|
||||
use-sync-external-store@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.1.0.tgz#3343c3fe7f7e404db70f8c687adf5c1652d34e82"
|
||||
integrity sha512-SEnieB2FPKEVne66NpXPd1Np4R1lTNKfjuy3XdIoPQKYBAFdzbzSZlSn1KJZUiihQLQC5Znot4SBz1EOTBwQAQ==
|
||||
|
||||
use@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||
@ -22130,6 +22135,13 @@ zustand@^4.0.0-beta.2:
|
||||
dependencies:
|
||||
use-sync-external-store "1.0.0"
|
||||
|
||||
zustand@^4.0.0-rc.1:
|
||||
version "4.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.0.0-rc.1.tgz#ec30a3afc03728adec7e1bd7bcc3592176372201"
|
||||
integrity sha512-qgcs7zLqBdHu0PuT3GW4WCIY5SgXdsv30GQMu9Qpp1BA2aS+sNS8l4x0hWuyEhjXkN+701aGWawhKDv6oWJAcw==
|
||||
dependencies:
|
||||
use-sync-external-store "1.1.0"
|
||||
|
||||
zwitch@^1.0.0:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"
|
||||
|
Loading…
Reference in New Issue
Block a user