Fix/Default market and tabs navigation (#518)

* fix: dont use localstorage for navigation, remove query params for tabs

* chore: lint

* fix: revert to using url rather than data test id

* chore: lint

* chore: remove arrow down ref from markets page object
This commit is contained in:
Matthew Russell 2022-06-08 01:47:31 -07:00 committed by GitHub
parent ca3a2905dd
commit be3b416176
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 21 additions and 101 deletions

View File

@ -20,11 +20,10 @@ export default class BasePage {
.should('be.visible') .should('be.visible')
.click({ force: true }); .click({ force: true });
cy.url().should('include', '/portfolio'); cy.url().should('include', '/portfolio');
cy.getByTestId('portfolio');
} }
navigateToMarkets() { navigateToMarkets() {
cy.getByTestId('markets-link').should('be.visible').click({ force: true }); cy.get(`a[href='${this.marketsUrl}']`).should('be.visible').click();
cy.url().should('include', '/markets'); cy.url().should('include', '/markets');
} }

View File

@ -8,7 +8,6 @@ export default class MarketPage extends BasePage {
marketRowPrices = 'flash-cell'; marketRowPrices = 'flash-cell';
marketRowDescription = 'name'; marketRowDescription = 'name';
marketStateColId = 'data'; marketStateColId = 'data';
openMarketMenu = 'arrow-down';
validateMarketsAreDisplayed() { validateMarketsAreDisplayed() {
// We need this to ensure that ag-grid is fully rendered before asserting // We need this to ensure that ag-grid is fully rendered before asserting
@ -57,13 +56,5 @@ export default class MarketPage extends BasePage {
clickOnMarket(text: string) { clickOnMarket(text: string) {
cy.get(`[col-id=${this.marketStateColId}]`).should('be.visible'); cy.get(`[col-id=${this.marketStateColId}]`).should('be.visible');
cy.get(`[col-id=${this.marketStateColId}]`).contains(text).click(); cy.get(`[col-id=${this.marketStateColId}]`).contains(text).click();
cy.url({ timeout: 8000 }).should(
'contain',
'portfolio=orders&trade=orderbook'
);
}
clickOpenMarketMenu() {
cy.getByTestId(this.openMarketMenu).click();
} }
} }

View File

@ -19,7 +19,6 @@ const mockMarkets = () => {
Then('I navigate to markets page', () => { Then('I navigate to markets page', () => {
mockMarkets(); mockMarkets();
marketsPage.navigateToMarkets(); marketsPage.navigateToMarkets();
marketsPage.clickOpenMarketMenu();
cy.wait('@Markets'); cy.wait('@Markets');
}); });

View File

@ -1,38 +1,17 @@
import * as Tabs from '@radix-ui/react-tabs'; import * as Tabs from '@radix-ui/react-tabs';
import classNames from 'classnames'; import classNames from 'classnames';
import { useRouter } from 'next/router';
import type { ReactElement, ReactNode } from 'react'; import type { ReactElement, ReactNode } from 'react';
import { Children, isValidElement, useEffect, useState } from 'react'; import { Children, isValidElement, useState } from 'react';
interface GridTabsProps { interface GridTabsProps {
children: ReactElement<GridTabProps>[]; children: ReactElement<GridTabProps>[];
group: string;
} }
export const GridTabs = ({ children, group }: GridTabsProps) => { export const GridTabs = ({ children }: GridTabsProps) => {
const { query, asPath, replace } = useRouter();
const [activeTab, setActiveTab] = useState<string>(() => { const [activeTab, setActiveTab] = useState<string>(() => {
const tab = query[group];
if (typeof tab === 'string') {
return tab;
}
// Default to first tab
return children[0].props.id; return children[0].props.id;
}); });
// Update the query string in the url when the active tab changes
// uses group property as the query string key
useEffect(() => {
const [url, queryString] = asPath.split('?');
const searchParams = new URLSearchParams(queryString);
searchParams.set(group, activeTab as string);
replace(`${url}?${searchParams.toString()}`);
// replace and using asPath causes a render loop
// eslint-disable-next-line
}, [activeTab, group]);
return ( return (
<Tabs.Root <Tabs.Root
value={activeTab} value={activeTab}

View File

@ -2,38 +2,9 @@ import { useRouter } from 'next/router';
import { Vega } from '../icons/vega'; import { Vega } from '../icons/vega';
import Link from 'next/link'; import Link from 'next/link';
import { AnchorButton } from '@vegaprotocol/ui-toolkit'; import { AnchorButton } from '@vegaprotocol/ui-toolkit';
import { LocalStorage, t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { useEffect, useState } from 'react';
export const Navbar = () => { export const Navbar = () => {
const initNavItemsState = [
{
name: t('Portfolio'),
path: '/portfolio',
testId: 'portfolio-link',
slug: '',
},
];
const [navItems, setNavItems] = useState(initNavItemsState);
const marketId = LocalStorage.getItem('marketId') ?? '';
useEffect(() => {
setNavItems([
{
name: t('Trading'),
path: '/markets',
testId: 'markets-link',
slug: marketId,
},
{
name: t('Portfolio'),
path: '/portfolio',
testId: 'portfolio-link',
slug: '',
},
]);
}, [marketId]);
return ( return (
<nav className="flex items-center"> <nav className="flex items-center">
<Link href="/" passHref={true}> <Link href="/" passHref={true}>
@ -41,7 +12,10 @@ export const Navbar = () => {
<Vega className="fill-black dark:fill-white" /> <Vega className="fill-black dark:fill-white" />
</a> </a>
</Link> </Link>
{navItems.map((route) => ( {[
{ name: t('Trading'), path: '/markets' },
{ name: t('Portfolio'), path: '/portfolio' },
].map((route) => (
<NavLink key={route.path} {...route} /> <NavLink key={route.path} {...route} />
))} ))}
</nav> </nav>
@ -53,30 +27,18 @@ interface NavLinkProps {
path: string; path: string;
exact?: boolean; exact?: boolean;
testId?: string; testId?: string;
slug?: string;
} }
const NavLink = ({ const NavLink = ({ name, path, exact, testId = name }: NavLinkProps) => {
name,
path,
exact,
testId = name,
slug = '',
}: NavLinkProps) => {
const router = useRouter(); const router = useRouter();
const isActive = const isActive =
router.asPath === path || (!exact && router.asPath.startsWith(path)); router.asPath === path || (!exact && router.asPath.startsWith(path));
const href = slug !== '' ? `${path}/${slug}` : path;
return ( return (
<AnchorButton <AnchorButton
variant={isActive ? 'accent' : 'inline'} variant={isActive ? 'accent' : 'inline'}
className="px-16 py-6 h-[38px] uppercase border-0 self-end xs:text-ui sm:text-body-large md:text-h5 lg:text-h4" className="px-16 py-6 h-[38px] uppercase border-0 self-end xs:text-ui sm:text-body-large md:text-h5 lg:text-h4"
data-testid={testId} data-testid={testId}
href={href} href={path}
onClick={(e) => {
e.preventDefault();
router.push(href);
}}
> >
{name} {name}
</AnchorButton> </AnchorButton>

View File

@ -1,5 +1,4 @@
import { gql, useQuery } from '@apollo/client'; import { gql, useQuery } from '@apollo/client';
import { LocalStorage } from '@vegaprotocol/react-helpers';
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';
@ -36,13 +35,10 @@ export function Index() {
// 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);
const setLandingDialog = useGlobalStore((state) => state.setLandingDialog); const setLandingDialog = useGlobalStore((state) => state.setLandingDialog);
const lastSelectedMarketId = LocalStorage.getItem('marketId');
useEffect(() => { useEffect(() => {
if (data) { if (data) {
const marketId = lastSelectedMarketId const marketId = marketList(data)[0]?.id;
? lastSelectedMarketId
: marketList(data)[0]?.id;
// If a default market is found, go to it with the landing dialog open // If a default market is found, go to it with the landing dialog open
if (marketId) { if (marketId) {
@ -54,7 +50,7 @@ export function Index() {
replace('/markets'); replace('/markets');
} }
} }
}, [data, lastSelectedMarketId, replace, setLandingDialog]); }, [data, replace, setLandingDialog]);
return ( return (
<AsyncRenderer data={data} loading={loading} error={error}> <AsyncRenderer data={data} loading={loading} error={error}>

View File

@ -5,7 +5,7 @@ import React, { useEffect, useState } from 'react';
import debounce from 'lodash/debounce'; 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 { LocalStorage, t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { useGlobalStore } from '../../stores'; import { useGlobalStore } from '../../stores';
import { LandingDialog } from '@vegaprotocol/market-list'; import { LandingDialog } from '@vegaprotocol/market-list';
import type { Market, MarketVariables } from './__generated__/Market'; import type { Market, MarketVariables } from './__generated__/Market';
@ -74,7 +74,6 @@ const MarketPage = ({ id }: { id?: string }) => {
); );
} }
LocalStorage.setItem('marketId', marketId);
return ( return (
<PageQueryContainer<Market, MarketVariables> <PageQueryContainer<Market, MarketVariables>
query={MARKET_QUERY} query={MARKET_QUERY}

View File

@ -99,7 +99,7 @@ export const TradeGrid = ({ market }: TradeGridProps) => {
<TradeMarketHeader market={market} /> <TradeMarketHeader market={market} />
<div className={wrapperClasses}> <div className={wrapperClasses}>
<TradeGridChild className="row-start-1 row-end-3"> <TradeGridChild className="row-start-1 row-end-3">
<GridTabs group="chart"> <GridTabs>
<GridTab id="candles" name={t('Candles')}> <GridTab id="candles" name={t('Candles')}>
<TradingViews.Candles marketId={market.id} /> <TradingViews.Candles marketId={market.id} />
</GridTab> </GridTab>
@ -112,7 +112,7 @@ export const TradeGrid = ({ market }: TradeGridProps) => {
<TradingViews.Ticket marketId={market.id} /> <TradingViews.Ticket marketId={market.id} />
</TradeGridChild> </TradeGridChild>
<TradeGridChild className="row-start-1 row-end-3"> <TradeGridChild className="row-start-1 row-end-3">
<GridTabs group="trade"> <GridTabs>
<GridTab id="trades" name={t('Trades')}> <GridTab id="trades" name={t('Trades')}>
<TradingViews.Trades marketId={market.id} /> <TradingViews.Trades marketId={market.id} />
</GridTab> </GridTab>
@ -122,7 +122,7 @@ export const TradeGrid = ({ market }: TradeGridProps) => {
</GridTabs> </GridTabs>
</TradeGridChild> </TradeGridChild>
<TradeGridChild className="col-span-3"> <TradeGridChild className="col-span-3">
<GridTabs group="portfolio"> <GridTabs>
<GridTab id="orders" name={t('Orders')}> <GridTab id="orders" name={t('Orders')}>
<TradingViews.Orders /> <TradingViews.Orders />
</GridTab> </GridTab>

View File

@ -21,7 +21,7 @@ const Portfolio = () => {
</h2> </h2>
</aside> </aside>
<section> <section>
<GridTabs group="portfolio"> <GridTabs>
<GridTab id="positions" name={t('Positions')}> <GridTab id="positions" name={t('Positions')}>
<div className={tabClassName}> <div className={tabClassName}>
<h4 className="text-h4 text-black dark:text-white"> <h4 className="text-h4 text-black dark:text-white">
@ -56,7 +56,7 @@ const Portfolio = () => {
</section> </section>
</main> </main>
<section className="fixed bottom-0 left-0 w-full h-[200px]"> <section className="fixed bottom-0 left-0 w-full h-[200px]">
<GridTabs group="collaterals"> <GridTabs>
<GridTab id="collateral" name={t('Collateral')}> <GridTab id="collateral" name={t('Collateral')}>
<AccountsContainer /> <AccountsContainer />
</GridTab> </GridTab>

View File

@ -60,7 +60,7 @@ export const CandlesChartContainer = ({
return ( return (
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">
<div className="px-8 flex flex-row flex-wrap gap-8"> <div className="p-8 flex flex-row flex-wrap gap-8">
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild={true}> <DropdownMenuTrigger asChild={true}>
<Button appendIconName="caret-down" variant="secondary"> <Button appendIconName="caret-down" variant="secondary">

View File

@ -50,10 +50,7 @@ export const SelectMarketList = ({
className={`hover:bg-black/20 dark:hover:bg-white/20 cursor-pointer relative`} className={`hover:bg-black/20 dark:hover:bg-white/20 cursor-pointer relative`}
> >
<td className={`${boldUnderlineClassNames} relative`}> <td className={`${boldUnderlineClassNames} relative`}>
<Link <Link href={`/markets/${id}`} passHref={true}>
href={`/markets/${id}?portfolio=orders&trade=orderbook&chart=candles`}
passHref={true}
>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a <a
onClick={() => onSelect(id)} onClick={() => onSelect(id)}

View File

@ -56,9 +56,7 @@ export const MarketsContainer = () => {
<MarketListTable <MarketListTable
ref={gridRef} ref={gridRef}
data={data} data={data}
onRowClicked={(id) => onRowClicked={(id) => push(`/markets/${id}`)}
push(`/markets/${id}?portfolio=orders&trade=orderbook`)
}
/> />
</AsyncRenderer> </AsyncRenderer>
); );