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() {
|
validateConnectWalletText() {
|
||||||
cy.getByTestId(this.connectVegaWalletText).should(
|
cy.getByTestId(this.connectVegaWalletText).should(
|
||||||
'have.text',
|
'have.text',
|
||||||
'Please connect your Vega wallet'
|
'Connect your Vega wallet'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
import { Given } from 'cypress-cucumber-preprocessor/steps';
|
import { Given } from 'cypress-cucumber-preprocessor/steps';
|
||||||
|
import { hasOperationName } from '..';
|
||||||
|
import { generateMarketList } from '../mocks/generate-market-list';
|
||||||
import BasePage from '../pages/base-page';
|
import BasePage from '../pages/base-page';
|
||||||
|
|
||||||
const basePage = new BasePage();
|
const basePage = new BasePage();
|
||||||
|
|
||||||
Given('I am on the homepage', () => {
|
Given('I am on the homepage', () => {
|
||||||
|
cy.mockGQL('MarketsList', (req) => {
|
||||||
|
if (hasOperationName(req, 'MarketsList')) {
|
||||||
|
req.reply({
|
||||||
|
body: { data: generateMarketList() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
basePage.closeDialog();
|
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();
|
const { isActive, error, connector, chainId } = useWeb3React();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (connector?.connectEagerly) {
|
||||||
connector?.connectEagerly &&
|
|
||||||
// Dont eager connect if this is a cypress test run
|
|
||||||
'Cypress' in window
|
|
||||||
) {
|
|
||||||
connector.connectEagerly();
|
connector.connectEagerly();
|
||||||
}
|
}
|
||||||
}, [connector]);
|
}, [connector]);
|
||||||
|
@ -9,20 +9,18 @@ import {
|
|||||||
} from '@vegaprotocol/wallet';
|
} from '@vegaprotocol/wallet';
|
||||||
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
|
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
|
||||||
import { Connectors } from '../lib/vega-connectors';
|
import { Connectors } from '../lib/vega-connectors';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { createClient } from '../lib/apollo-client';
|
import { createClient } from '../lib/apollo-client';
|
||||||
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
||||||
import { ApolloProvider } from '@apollo/client';
|
import { ApolloProvider } from '@apollo/client';
|
||||||
import { AppLoader } from '../components/app-loader';
|
import { AppLoader } from '../components/app-loader';
|
||||||
import { VegaWalletConnectButton } from '../components/vega-wallet-connect-button';
|
import { VegaWalletConnectButton } from '../components/vega-wallet-connect-button';
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
|
import { useGlobalStore } from '../stores';
|
||||||
|
|
||||||
function VegaTradingApp({ Component, pageProps }: AppProps) {
|
function VegaTradingApp({ Component, pageProps }: AppProps) {
|
||||||
const client = useMemo(() => createClient(process.env['NX_VEGA_URL']), []);
|
const client = useMemo(() => createClient(process.env['NX_VEGA_URL']), []);
|
||||||
const [vegaWallet, setVegaWallet] = useState({
|
const store = useGlobalStore();
|
||||||
connect: false,
|
|
||||||
manage: false,
|
|
||||||
});
|
|
||||||
const [theme, toggleTheme] = useThemeSwitcher();
|
const [theme, toggleTheme] = useThemeSwitcher();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -55,12 +53,12 @@ function VegaTradingApp({ Component, pageProps }: AppProps) {
|
|||||||
<Navbar />
|
<Navbar />
|
||||||
<div className="flex items-center gap-4 ml-auto mr-8">
|
<div className="flex items-center gap-4 ml-auto mr-8">
|
||||||
<VegaWalletConnectButton
|
<VegaWalletConnectButton
|
||||||
setConnectDialog={(open) =>
|
setConnectDialog={(open) => {
|
||||||
setVegaWallet((x) => ({ ...x, connect: open }))
|
store.setVegaWalletConnectDialog(open);
|
||||||
}
|
}}
|
||||||
setManageDialog={(open) =>
|
setManageDialog={(open) => {
|
||||||
setVegaWallet((x) => ({ ...x, manage: open }))
|
store.setVegaWalletManageDialog(open);
|
||||||
}
|
}}
|
||||||
/>
|
/>
|
||||||
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" />
|
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" />
|
||||||
</div>
|
</div>
|
||||||
@ -71,15 +69,15 @@ function VegaTradingApp({ Component, pageProps }: AppProps) {
|
|||||||
</main>
|
</main>
|
||||||
<VegaConnectDialog
|
<VegaConnectDialog
|
||||||
connectors={Connectors}
|
connectors={Connectors}
|
||||||
dialogOpen={vegaWallet.connect}
|
dialogOpen={store.vegaWalletConnectDialog}
|
||||||
setDialogOpen={(open) =>
|
setDialogOpen={(open) =>
|
||||||
setVegaWallet((x) => ({ ...x, connect: open }))
|
store.setVegaWalletConnectDialog(open)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<VegaManageDialog
|
<VegaManageDialog
|
||||||
dialogOpen={vegaWallet.manage}
|
dialogOpen={store.vegaWalletManageDialog}
|
||||||
setDialogOpen={(open) =>
|
setDialogOpen={(open) =>
|
||||||
setVegaWallet((x) => ({ ...x, manage: open }))
|
store.setVegaWalletManageDialog(open)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { gql, useQuery } from '@apollo/client';
|
import { gql, useQuery } from '@apollo/client';
|
||||||
import { LandingDialog } from '@vegaprotocol/market-list';
|
|
||||||
import { MarketTradingMode } from '@vegaprotocol/types';
|
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import sortBy from 'lodash/sortBy';
|
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';
|
import type { MarketsLanding } from './__generated__/MarketsLanding';
|
||||||
|
|
||||||
const MARKETS_QUERY = gql`
|
const MARKETS_QUERY = gql`
|
||||||
@ -29,24 +30,33 @@ const marketList = ({ markets }: MarketsLanding) =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
export function Index() {
|
export function Index() {
|
||||||
|
const { replace } = useRouter();
|
||||||
// The default market selected in the platform behind the overlay
|
// 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).
|
// 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);
|
const { data, error, loading } = useQuery<MarketsLanding>(MARKETS_QUERY);
|
||||||
if (data && !error && !loading) {
|
const setLandingDialog = useGlobalStore((state) => state.setLandingDialog);
|
||||||
const marketId = marketList(data)[0]?.id;
|
|
||||||
window.history.replaceState(
|
useEffect(() => {
|
||||||
data,
|
if (data) {
|
||||||
'',
|
const marketId = marketList(data)[0]?.id;
|
||||||
marketId ? `/markets/${marketId}` : '/markets'
|
|
||||||
);
|
// 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 (
|
return (
|
||||||
<>
|
<AsyncRenderer data={data} loading={loading} error={error}>
|
||||||
<LandingDialog />
|
{/* Render a loading and error state but we will redirect if markets are found */}
|
||||||
<AsyncRenderer data={data} error={error} loading={loading}>
|
{null}
|
||||||
<MarketPage id={data && marketList(data)[0]?.id} />
|
</AsyncRenderer>
|
||||||
</AsyncRenderer>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,8 @@ import debounce from 'lodash/debounce';
|
|||||||
import { PageQueryContainer } from '../../components/page-query-container';
|
import { PageQueryContainer } from '../../components/page-query-container';
|
||||||
import { TradeGrid, TradePanels } from './trade-grid';
|
import { TradeGrid, TradePanels } from './trade-grid';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
import { useGlobalStore } from '../../stores';
|
||||||
|
import { LandingDialog } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
// Top level page query
|
// Top level page query
|
||||||
const MARKET_QUERY = gql`
|
const MARKET_QUERY = gql`
|
||||||
@ -21,6 +23,7 @@ const MARKET_QUERY = gql`
|
|||||||
const MarketPage = ({ id }: { id?: string }) => {
|
const MarketPage = ({ id }: { id?: string }) => {
|
||||||
const { query } = useRouter();
|
const { query } = useRouter();
|
||||||
const { w } = useWindowSize();
|
const { w } = useWindowSize();
|
||||||
|
const store = useGlobalStore();
|
||||||
|
|
||||||
// Default to first marketId query item if found
|
// Default to first marketId query item if found
|
||||||
const marketId =
|
const marketId =
|
||||||
@ -48,10 +51,18 @@ const MarketPage = ({ id }: { id?: string }) => {
|
|||||||
return <Splash>{t('Market not found')}</Splash>;
|
return <Splash>{t('Market not found')}</Splash>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return w > 960 ? (
|
return (
|
||||||
<TradeGrid market={market} />
|
<>
|
||||||
) : (
|
{w > 960 ? (
|
||||||
<TradePanels market={market} />
|
<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 { useRouter } from 'next/router';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { WithdrawPageContainer } from './withdraw-page-container';
|
import { WithdrawPageContainer } from './withdraw-page-container';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
import { VegaWalletContainer } from '../../../components/vega-wallet-container';
|
||||||
|
import { Web3Container } from '../../../components/web3-container';
|
||||||
|
|
||||||
const Withdraw = () => {
|
const Withdraw = () => {
|
||||||
const { query } = useRouter();
|
const { query } = useRouter();
|
||||||
@ -21,14 +22,16 @@ const Withdraw = () => {
|
|||||||
}, [query]);
|
}, [query]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Web3Container
|
<VegaWalletContainer>
|
||||||
render={() => (
|
<Web3Container
|
||||||
<div className="max-w-[420px] p-24 mx-auto">
|
render={() => (
|
||||||
<h1 className="text-h3 mb-12">{t('Withdraw')}</h1>
|
<div className="max-w-[420px] p-24 mx-auto">
|
||||||
<WithdrawPageContainer assetId={assetId} />
|
<h1 className="text-h3 mb-12">{t('Withdraw')}</h1>
|
||||||
</div>
|
<WithdrawPageContainer assetId={assetId} />
|
||||||
)}
|
</div>
|
||||||
/>
|
)}
|
||||||
|
/>
|
||||||
|
</VegaWalletContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,14 +47,6 @@ export const WithdrawPageContainer = ({
|
|||||||
}: WithdrawPageContainerProps) => {
|
}: WithdrawPageContainerProps) => {
|
||||||
const { keypair } = useVegaWallet();
|
const { keypair } = useVegaWallet();
|
||||||
|
|
||||||
if (!keypair) {
|
|
||||||
return (
|
|
||||||
<p data-testid="connect-vega-wallet-text">
|
|
||||||
{t('Please connect your Vega wallet')}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageQueryContainer<WithdrawPageQuery, WithdrawPageQueryVariables>
|
<PageQueryContainer<WithdrawPageQuery, WithdrawPageQueryVariables>
|
||||||
query={WITHDRAW_PAGE_QUERY}
|
query={WITHDRAW_PAGE_QUERY}
|
||||||
|
@ -1,30 +1,26 @@
|
|||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import { AnchorButton, Splash } from '@vegaprotocol/ui-toolkit';
|
import { AnchorButton } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { VegaWalletContainer } from '../../../components/vega-wallet-container';
|
||||||
import { Web3Container } from '../../../components/web3-container';
|
import { Web3Container } from '../../../components/web3-container';
|
||||||
import { WithdrawalsPageContainer } from './withdrawals-page-container';
|
import { WithdrawalsPageContainer } from './withdrawals-page-container';
|
||||||
|
|
||||||
const Withdrawals = () => {
|
const Withdrawals = () => {
|
||||||
const { keypair } = useVegaWallet();
|
|
||||||
|
|
||||||
if (!keypair) {
|
|
||||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Web3Container
|
<VegaWalletContainer>
|
||||||
render={() => (
|
<Web3Container
|
||||||
<div className="h-full grid grid grid-rows-[min-content,1fr]">
|
render={() => (
|
||||||
<header className="flex justify-between p-24">
|
<div className="h-full grid grid grid-rows-[min-content,1fr]">
|
||||||
<h1 className="text-h3">{t('Withdrawals')}</h1>
|
<header className="flex justify-between p-24">
|
||||||
<AnchorButton href="/portfolio/withdraw">
|
<h1 className="text-h3">{t('Withdrawals')}</h1>
|
||||||
{t('Start withdrawal')}
|
<AnchorButton href="/portfolio/withdraw">
|
||||||
</AnchorButton>
|
{t('Start withdrawal')}
|
||||||
</header>
|
</AnchorButton>
|
||||||
<WithdrawalsPageContainer />
|
</header>
|
||||||
</div>
|
<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 { t } from '@vegaprotocol/react-helpers';
|
||||||
import { Interval } from '@vegaprotocol/types';
|
import { Interval } from '@vegaprotocol/types';
|
||||||
import { AsyncRenderer, Dialog, Intent } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, Dialog, Intent } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useState } from 'react';
|
|
||||||
import { MARKET_LIST_QUERY } from '../markets-container/markets-data-provider';
|
import { MARKET_LIST_QUERY } from '../markets-container/markets-data-provider';
|
||||||
import type { MarketList } from '../markets-container/__generated__/MarketList';
|
import type { MarketList } from '../markets-container/__generated__/MarketList';
|
||||||
|
|
||||||
import { SelectMarketList } from './select-market-list';
|
import { SelectMarketList } from './select-market-list';
|
||||||
|
|
||||||
export const LandingDialog = () => {
|
interface LandingDialogProps {
|
||||||
const [open, setOpen] = useState(true);
|
open: boolean;
|
||||||
|
setOpen: (open: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LandingDialog = ({ open, setOpen }: LandingDialogProps) => {
|
||||||
const setClose = () => setOpen(false);
|
const setClose = () => setOpen(false);
|
||||||
|
|
||||||
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600;
|
||||||
@ -21,17 +24,15 @@ export const LandingDialog = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||||
{
|
<Dialog
|
||||||
<Dialog
|
title={t('Select a market to get started')}
|
||||||
title={t('Select a market to get started')}
|
intent={Intent.Prompt}
|
||||||
intent={Intent.Prompt}
|
open={open}
|
||||||
open={open}
|
onChange={setClose}
|
||||||
onChange={setClose}
|
titleClassNames="font-bold font-sans text-3xl tracking-tight mb-0 pl-8"
|
||||||
titleClassNames="font-bold font-sans text-3xl tracking-tight mb-0 pl-8"
|
>
|
||||||
>
|
<SelectMarketList data={data} onSelect={setClose} />
|
||||||
<SelectMarketList data={data} />
|
</Dialog>
|
||||||
</Dialog>
|
|
||||||
}
|
|
||||||
</AsyncRenderer>
|
</AsyncRenderer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,41 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
import type { MarketList } from '../__generated__/MarketList';
|
import type { ReactNode } from 'react';
|
||||||
|
import type { MarketList } from '../markets-container/__generated__/MarketList';
|
||||||
import { SelectMarketList } from './select-market-list';
|
import { SelectMarketList } from './select-market-list';
|
||||||
|
|
||||||
|
jest.mock(
|
||||||
|
'next/link',
|
||||||
|
() =>
|
||||||
|
({ children }: { children: ReactNode }) =>
|
||||||
|
children
|
||||||
|
);
|
||||||
|
|
||||||
describe('SelectMarketList', () => {
|
describe('SelectMarketList', () => {
|
||||||
it('should render', () => {
|
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('AAPL.MF21')).toBeTruthy();
|
||||||
expect(screen.getByText('-3.14%')).toBeTruthy();
|
expect(screen.getByText('-3.14%')).toBeTruthy();
|
||||||
expect(screen.getByText('141.75')).toBeTruthy();
|
expect(screen.getByText('141.75')).toBeTruthy();
|
||||||
expect(screen.getByText('Or view full market list')).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 = {
|
const mockData = {
|
||||||
|
@ -10,11 +10,12 @@ import type { MarketList } from '../markets-container/__generated__/MarketList';
|
|||||||
|
|
||||||
export interface SelectMarketListProps {
|
export interface SelectMarketListProps {
|
||||||
data: MarketList | undefined;
|
data: MarketList | undefined;
|
||||||
|
onSelect: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CandleClose = Required<string>;
|
type CandleClose = Required<string>;
|
||||||
|
|
||||||
export const SelectMarketList = ({ data }: SelectMarketListProps) => {
|
export const SelectMarketList = ({ data, onSelect }: SelectMarketListProps) => {
|
||||||
const thClassNames = (direction: 'left' | 'right') =>
|
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`;
|
`px-8 text-${direction} font-sans font-normal text-ui-small leading-9 mb-0 text-dark/80 dark:text-white/80`;
|
||||||
const tdClassNames =
|
const tdClassNames =
|
||||||
@ -49,8 +50,15 @@ export const SelectMarketList = ({ data }: SelectMarketListProps) => {
|
|||||||
<td className={`${boldUnderlineClassNames} relative`}>
|
<td className={`${boldUnderlineClassNames} relative`}>
|
||||||
<Link
|
<Link
|
||||||
href={`/markets/${id}?portfolio=orders&trade=orderbook&chart=candles`}
|
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>
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
<td className={tdClassNames}>
|
<td className={tdClassNames}>
|
||||||
|
@ -2,3 +2,4 @@ export * from './market-list-table';
|
|||||||
export * from './markets-container';
|
export * from './markets-container';
|
||||||
export * from './markets-data-provider';
|
export * from './markets-data-provider';
|
||||||
export * from './summary-cell';
|
export * from './summary-cell';
|
||||||
|
export * from './__generated__/MarketList';
|
||||||
|
@ -71,7 +71,8 @@
|
|||||||
"tailwindcss": "^3.0.23",
|
"tailwindcss": "^3.0.23",
|
||||||
"tslib": "^2.0.0",
|
"tslib": "^2.0.0",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4",
|
||||||
|
"zustand": "^4.0.0-rc.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@apollo/react-testing": "^4.0.0",
|
"@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"
|
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.0.0.tgz#d98f4a9c2e73d0f958e7e2d2c2bfb5f618cbd8fd"
|
||||||
integrity sha512-AFVsxg5GkFg8GDcxnl+Z0lMAz9rE8DGJCc28qnBuQF7lac57B5smLcT37aXpXIIPz75rW4g3eXHPjhHwdGskOw==
|
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:
|
use@^3.1.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||||
@ -22130,6 +22135,13 @@ zustand@^4.0.0-beta.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
use-sync-external-store "1.0.0"
|
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:
|
zwitch@^1.0.0:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"
|
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"
|
||||||
|
Loading…
Reference in New Issue
Block a user