Compare commits

..
47 changed files with 264 additions and 450 deletions
@@ -24,7 +24,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
const navigate = useNavigate();
const ref = useRef<AgGridReact>(null);
const showColumnsOnDesktop = () => {
ref.current?.api.setColumnsVisible(
ref.current?.columnApi.setColumnsVisible(
['id', 'type', 'status'],
window.innerWidth > BREAKPOINT_MD
);
@@ -29,7 +29,7 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
const gridRef = useRef<AgGridReact>(null);
useLayoutEffect(() => {
const showColumnsOnDesktop = () => {
gridRef.current?.api.setColumnsVisible(
gridRef.current?.columnApi.setColumnsVisible(
['id', 'state', 'asset'],
window.innerWidth > BREAKPOINT_MD
);
@@ -44,11 +44,11 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
const gridRef = useRef<AgGridReact>(null);
useLayoutEffect(() => {
const showColumnsOnDesktop = () => {
gridRef.current?.api.setColumnsVisible(
gridRef.current?.columnApi.setColumnsVisible(
['voting', 'cDate', 'eDate', 'type'],
window.innerWidth > BREAKPOINT_MD
);
gridRef.current?.api.setColumnWidth(
gridRef.current?.columnApi.setColumnWidth(
'actions',
window.innerWidth > BREAKPOINT_MD ? 221 : 80
);
@@ -54,7 +54,7 @@ const Block = () => {
</Button>
</Link>
</div>
{blockData && (
{blockData && 'result' in blockData && (
<>
<TableWithTbody className="mb-8">
<TableRow modifier="bordered">
@@ -95,7 +95,7 @@ export const ProtocolUpgradeProposalContainer = () => {
time={
pending && time ? (
convertToCountdownString(time, '0:00:00:00')
) : blockInfo?.result ? (
) : blockInfo && 'result' in blockInfo && blockInfo?.result ? (
<span title={blockInfo.result.block.header.time}>
{formatDateWithLocalTimezone(
new Date(blockInfo.result.block.header.time)
@@ -9,8 +9,6 @@ import { TradeGrid } from './trade-grid';
import { TradePanels } from './trade-panels';
import { useNavigate, useParams } from 'react-router-dom';
import { Links } from '../../lib/links';
import { ViewType, useSidebar } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT, ns } from '../../lib/use-t';
import { Trans } from 'react-i18next';
@@ -61,9 +59,6 @@ export const MarketPage = () => {
const t = useT();
const { marketId } = useParams();
const navigate = useNavigate();
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);
@@ -77,12 +72,6 @@ export const MarketPage = () => {
}
}, [update, lastMarketId, data?.id]);
useEffect(() => {
if (largeScreen && view === undefined) {
setViews({ type: ViewType.Order }, currentRouteId);
}
}, [setViews, view, currentRouteId, largeScreen]);
const pinnedAsset = data && getAsset(data);
const tradeView = useMemo(() => {
@@ -90,11 +90,7 @@ const MainGrid = memo(
{market &&
market.tradableInstrument.instrument.product.__typename ===
'Perpetual' ? (
<Tab
id="funding-payments"
name={t('Funding payments')}
settings={<TradingViews.fundingPayments.settings />}
>
<Tab id="funding-payments" name={t('Funding payments')}>
<ErrorBoundary feature="funding-payments">
<TradingViews.fundingPayments.component
marketId={marketId}
@@ -116,11 +112,7 @@ const MainGrid = memo(
<TradingViews.orderbook.component marketId={marketId} />
</ErrorBoundary>
</Tab>
<Tab
id="trades"
name={t('Trades')}
settings={<TradingViews.trades.settings />}
>
<Tab id="trades" name={t('Trades')}>
<ErrorBoundary feature="trades">
<TradingViews.trades.component marketId={marketId} />
</ErrorBoundary>
@@ -141,7 +133,6 @@ const MainGrid = memo(
id="positions"
name={t('Positions')}
menu={<TradingViews.positions.menu />}
settings={<TradingViews.positions.settings />}
>
<ErrorBoundary feature="positions">
<TradingViews.positions.component />
@@ -151,26 +142,17 @@ const MainGrid = memo(
id="open-orders"
name={t('Open')}
menu={<TradingViews.activeOrders.menu />}
settings={<TradingViews.activeOrders.settings />}
>
<ErrorBoundary feature="activeOrders">
<TradingViews.activeOrders.component />
</ErrorBoundary>
</Tab>
<Tab
id="closed-orders"
name={t('Closed')}
settings={<TradingViews.closedOrders.settings />}
>
<Tab id="closed-orders" name={t('Closed')}>
<ErrorBoundary feature="closedOrders">
<TradingViews.closedOrders.component />
</ErrorBoundary>
</Tab>
<Tab
id="rejected-orders"
name={t('Rejected')}
settings={<TradingViews.rejectedOrders.settings />}
>
<Tab id="rejected-orders" name={t('Rejected')}>
<ErrorBoundary feature="rejectedOrders">
<TradingViews.rejectedOrders.component />
</ErrorBoundary>
@@ -179,35 +161,25 @@ const MainGrid = memo(
id="orders"
name={t('All')}
menu={<TradingViews.orders.menu />}
settings={<TradingViews.orders.settings />}
>
<ErrorBoundary feature="orders">
<TradingViews.orders.component />
</ErrorBoundary>
</Tab>
{featureFlags.STOP_ORDERS ? (
<Tab
id="stop-orders"
name={t('Stop orders')}
settings={<TradingViews.stopOrders.settings />}
>
<Tab id="stop-orders" name={t('Stop orders')}>
<ErrorBoundary feature="stop-orders">
<TradingViews.stopOrders.component />
</ErrorBoundary>
</Tab>
) : null}
<Tab
id="fills"
name={t('Fills')}
settings={<TradingViews.fills.settings />}
>
<Tab id="fills" name={t('Fills')}>
<TradingViews.fills.component />
</Tab>
<Tab
id="accounts"
name={t('Collateral')}
menu={<TradingViews.collateral.menu />}
settings={<TradingViews.collateral.settings />}
>
<ErrorBoundary feature="collateral">
<TradingViews.collateral.component
@@ -4,7 +4,7 @@ import { OracleBanner } from '@vegaprotocol/markets';
import { useState } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import classNames from 'classnames';
import { Splash, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useT } from '../../lib/use-t';
import {
MarketSuccessorBanner,
@@ -24,8 +24,6 @@ interface TradePanelsProps {
export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const [view, setView] = useState<TradingView>('chart');
const [settingsOpened, setSettingsOpened] = useState(false);
const viewCfg = TradingViews[view];
const renderView = () => {
const Component = TradingViews[view].component;
@@ -46,25 +44,23 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
};
const renderMenu = () => {
if ('menu' in viewCfg || 'settings' in viewCfg) {
const viewCfg = TradingViews[view];
if ('menu' in viewCfg) {
const Menu = viewCfg.menu;
return (
<div className="flex items-center justify-end gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
{'menu' in viewCfg ? <viewCfg.menu /> : null}
{'settings' in viewCfg ? (
<button
className="ml-1 flex items-center justify-center h-6 w-6"
onClick={() => setSettingsOpened((v) => !v)}
>
<VegaIcon name={VegaIconNames.COG} size={16} />
</button>
) : null}
<Menu />
</div>
);
}
return null;
};
return (
<div className="h-full grid grid-rows-[min-content_1fr_min-content]">
<div className="h-full grid grid-rows-[min-content_min-content_1fr_min-content]">
<div>
{featureFlags.SUCCESSOR_MARKETS && (
<>
@@ -75,36 +71,15 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
<MarketTerminationBanner market={market} />
<OracleBanner marketId={market?.id || ''} />
</div>
<div className="h-full grid grid-rows-[min-content_1fr] relative">
<div>{renderMenu()}</div>
<div className="h-full">
<AutoSizer>
{({ width, height }) => (
<div style={{ width, height }} className="overflow-auto">
{renderView()}
</div>
)}
</AutoSizer>
</div>
{settingsOpened && 'settings' in viewCfg ? (
<div className="absolute right-0 top-0 bottom-0 w-[280px] max-w-full bg-vega-clight-700 dark:bg-vega-cdark-700 z-10">
<div className="grid grid-rows-[32px_1fr]">
<div className="flex justify-end p-1">
<button
type="button"
data-testid="settings-close"
onClick={() => setSettingsOpened(false)}
className="flex items-center justify-center h-6 w-6"
>
<VegaIcon name={VegaIconNames.CROSS} size={12} />
</button>
</div>
<div className="relative h-full overflow-auto p-2">
<viewCfg.settings />
</div>
<div>{renderMenu()}</div>
<div className="h-full">
<AutoSizer>
{({ width, height }) => (
<div style={{ width, height }} className="overflow-auto">
{renderView()}
</div>
</div>
) : null}
)}
</AutoSizer>
</div>
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
{Object.keys(TradingViews)
@@ -135,10 +110,7 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
key={key}
view={key}
isActive={isActive}
onClick={() => {
setSettingsOpened(false);
setView(key);
}}
onClick={() => setView(key)}
/>
);
})}
@@ -1,36 +1,15 @@
import { DepthChartContainer } from '@vegaprotocol/market-depth';
import { Filter, OpenOrdersMenu } from '@vegaprotocol/orders';
import {
TradesContainer,
TradesSettings,
} from '../../components/trades-container';
import { TradesContainer } from '../../components/trades-container';
import { OrderbookContainer } from '../../components/orderbook-container';
import {
FillsContainer,
FillsSettings,
} from '../../components/fills-container';
import {
PositionsContainer,
PositionsSettings,
} from '../../components/positions-container';
import {
AccountsContainer,
AccountsSettings,
} from '../../components/accounts-container';
import { FillsContainer } from '../../components/fills-container';
import { PositionsContainer } from '../../components/positions-container';
import { AccountsContainer } from '../../components/accounts-container';
import { LiquidityContainer } from '../../components/liquidity-container';
import { FundingContainer } from '../../components/funding-container';
import {
FundingPaymentsContainer,
FundingPaymentsSettings,
} from '../../components/funding-payments-container';
import {
OrdersContainer,
OrdersSettings,
} from '../../components/orders-container';
import {
StopOrdersContainer,
StopOrdersSettings,
} from '../../components/stop-orders-container';
import { FundingPaymentsContainer } from '../../components/funding-payments-container';
import { OrdersContainer } from '../../components/orders-container';
import { StopOrdersContainer } from '../../components/stop-orders-container';
import { AccountsMenu } from '../../components/accounts-menu';
import { PositionsMenu } from '../../components/positions-menu';
import { ChartContainer, ChartMenu } from '../../components/chart-container';
@@ -53,46 +32,37 @@ export const TradingViews = {
},
fundingPayments: {
component: FundingPaymentsContainer,
settings: FundingPaymentsSettings,
},
orderbook: {
component: OrderbookContainer,
},
trades: {
component: TradesContainer,
settings: TradesSettings,
},
positions: {
component: PositionsContainer,
menu: PositionsMenu,
settings: PositionsSettings,
},
activeOrders: {
component: () => <OrdersContainer filter={Filter.Open} />,
menu: OpenOrdersMenu,
settings: () => <OrdersSettings filter={Filter.Open} />,
},
closedOrders: {
component: () => <OrdersContainer filter={Filter.Closed} />,
settings: () => <OrdersSettings filter={Filter.Closed} />,
},
rejectedOrders: {
component: () => <OrdersContainer filter={Filter.Rejected} />,
settings: () => <OrdersSettings filter={Filter.Rejected} />,
},
orders: {
component: OrdersContainer,
menu: OpenOrdersMenu,
settings: OrdersSettings,
},
stopOrders: {
component: StopOrdersContainer,
settings: StopOrdersSettings,
},
collateral: {
component: AccountsContainer,
menu: AccountsMenu,
settings: AccountsSettings,
},
fills: { component: FillsContainer, settings: FillsSettings },
fills: { component: FillsContainer },
} as const;
@@ -4,9 +4,26 @@ import {
SidebarButton,
SidebarDivider,
ViewType,
useSidebar,
} from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useEffect } from 'react';
const ViewInitializer = () => {
const currentRouteId = useGetCurrentRouteId();
const { setViews, getView } = useSidebar();
const view = getView(currentRouteId);
const { screenSize } = useScreenDimensions();
const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize);
useEffect(() => {
if (largeScreen && view === undefined) {
setViews({ type: ViewType.Order }, currentRouteId);
}
}, [setViews, view, currentRouteId, largeScreen]);
return null;
};
export const MarketsSidebar = () => {
const t = useT();
@@ -37,6 +54,7 @@ export const MarketsSidebar = () => {
path=":marketId"
element={
<>
<ViewInitializer />
<SidebarDivider />
<SidebarButton
view={ViewType.Order}
@@ -5,29 +5,14 @@ import { titlefy } from '@vegaprotocol/utils';
import { useIncompleteWithdrawals } from '@vegaprotocol/withdraws';
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
import { usePageTitleStore } from '../../stores';
import {
AccountsContainer,
AccountsSettings,
} from '../../components/accounts-container';
import { AccountsContainer } from '../../components/accounts-container';
import { DepositsContainer } from '../../components/deposits-container';
import {
FillsContainer,
FillsSettings,
} from '../../components/fills-container';
import {
FundingPaymentsContainer,
FundingPaymentsSettings,
} from '../../components/funding-payments-container';
import {
PositionsContainer,
PositionsSettings,
} from '../../components/positions-container';
import { FillsContainer } from '../../components/fills-container';
import { FundingPaymentsContainer } from '../../components/funding-payments-container';
import { PositionsContainer } from '../../components/positions-container';
import { PositionsMenu } from '../../components/positions-menu';
import { WithdrawalsContainer } from '../../components/withdrawals-container';
import {
OrdersContainer,
OrdersSettings,
} from '../../components/orders-container';
import { OrdersContainer } from '../../components/orders-container';
import { LedgerContainer } from '../../components/ledger-container';
import {
ResizableGrid,
@@ -54,11 +39,21 @@ const WithdrawalsIndicator = () => {
);
};
export const Portfolio = () => {
const t = useT();
const SidebarViewInitializer = () => {
const currentRouteId = useGetCurrentRouteId();
const { getView, setViews } = useSidebar();
const view = getView(currentRouteId);
// Make transfer sidebar open by default
useEffect(() => {
if (view === undefined) {
setViews({ type: ViewType.Transfer }, currentRouteId);
}
}, [view, setViews, currentRouteId]);
return null;
};
export const Portfolio = () => {
const t = useT();
const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle,
@@ -68,17 +63,11 @@ export const Portfolio = () => {
updateTitle(titlefy([t('Portfolio')]));
}, [updateTitle, t]);
// Make transfer sidebar open by default
useEffect(() => {
if (view === undefined) {
setViews({ type: ViewType.Transfer }, currentRouteId);
}
}, [view, setViews, currentRouteId]);
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col';
return (
<div className={wrapperClasses}>
<SidebarViewInitializer />
<ResizableGrid vertical onChange={handleOnLayoutChange}>
<ResizableGridPanel minSize={75}>
<PortfolioGridChild>
@@ -87,27 +76,22 @@ export const Portfolio = () => {
id="positions"
name={t('Positions')}
menu={<PositionsMenu />}
settings={<PositionsSettings />}
>
<ErrorBoundary feature="portfolio-positions">
<PositionsContainer allKeys />
</ErrorBoundary>
</Tab>
<Tab id="orders" name={t('Orders')} settings={<OrdersSettings />}>
<Tab id="orders" name={t('Orders')}>
<ErrorBoundary feature="portfolio-orders">
<OrdersContainer />
</ErrorBoundary>
</Tab>
<Tab id="fills" name={t('Fills')} settings={<FillsSettings />}>
<Tab id="fills" name={t('Fills')}>
<ErrorBoundary feature="portfolio-fills">
<FillsContainer />
</ErrorBoundary>
</Tab>
<Tab
id="funding-payments"
name={t('Funding payments')}
settings={<FundingPaymentsSettings />}
>
<Tab id="funding-payments" name={t('Funding payments')}>
<ErrorBoundary feature="portfolio-funding-payments">
<FundingPaymentsContainer />
</ErrorBoundary>
@@ -130,7 +114,6 @@ export const Portfolio = () => {
<Tab
id="collateral"
name={t('Collateral')}
settings={<AccountsSettings />}
menu={<AccountsMenu />}
>
<ErrorBoundary feature="portfolio-accounts">
@@ -73,7 +73,7 @@ export const AccountsContainer = ({
);
};
export const useAccountStore = create<DataGridSlice>()(
const useAccountStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_accounts_store',
})
@@ -1,8 +0,0 @@
import { GridSettings } from '../grid-settings/grid-settings';
import { useAccountStore } from './accounts-container';
export const AccountsSettings = () => (
<GridSettings
updateGridStore={useAccountStore((store) => store.updateGridStore)}
/>
);
@@ -1,2 +1 @@
export * from './accounts-container';
export * from './accounts-settings';
@@ -38,7 +38,7 @@ export const FillsContainer = () => {
);
};
export const useFillsStore = create<DataGridSlice>()(
const useFillsStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_fills_store',
})
@@ -1,8 +0,0 @@
import { GridSettings } from '../grid-settings/grid-settings';
import { useFillsStore } from './fills-container';
export const FillsSettings = () => (
<GridSettings
updateGridStore={useFillsStore((store) => store.updateGridStore)}
/>
);
@@ -1,2 +1 @@
export * from './fills-container';
export * from './fills-settings';
@@ -45,7 +45,7 @@ export const FundingPaymentsContainer = ({
);
};
export const useFundingPaymentsStore = create<DataGridSlice>()(
const useFundingPaymentsStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_funding_payments_store',
})
@@ -1,8 +0,0 @@
import { GridSettings } from '../grid-settings/grid-settings';
import { useFundingPaymentsStore } from './funding-payments-container';
export const FundingPaymentsSettings = () => (
<GridSettings
updateGridStore={useFundingPaymentsStore((store) => store.updateGridStore)}
/>
);
@@ -1,2 +1 @@
export * from './funding-payments-container';
export * from './funding-payments-settings';
@@ -1,24 +0,0 @@
import { useT } from '../../lib/use-t';
import type { DataGridStore } from '../../stores/datagrid-store-slice';
import { TradingButton as Button } from '@vegaprotocol/ui-toolkit';
export const GridSettings = ({
updateGridStore,
}: {
updateGridStore: (gridStore: DataGridStore) => void;
}) => {
const t = useT();
return (
<Button
onClick={() =>
updateGridStore({
columnState: undefined,
filterModel: undefined,
})
}
size="extra-small"
>
{t('Reset Columns')}
</Button>
);
};
@@ -1,5 +1,9 @@
import { act, renderHook } from '@testing-library/react';
import { STORAGE_KEY, useOrderListGridState } from './orders-container';
import {
FilterStatusValue,
STORAGE_KEY,
useOrderListGridState,
} from './orders-container';
import { Filter } from '@vegaprotocol/orders';
import { OrderType } from '@vegaprotocol/types';
@@ -12,6 +16,31 @@ describe('useOrderListGridState', () => {
return renderHook(() => useOrderListGridState(filter));
};
it.each(Object.values(Filter))(
'providers correct AgGrid filter for %s',
(filter) => {
const { result } = setup(filter);
expect(typeof result.current.updateGridState).toBe('function');
expect(result.current.gridState).toEqual({
columnState: undefined,
filterModel: {
status: {
value: FilterStatusValue[filter],
},
},
});
}
);
it('provides correct AgGrid filter for all', () => {
const { result } = setup(undefined);
expect(typeof result.current.updateGridState).toBe('function');
expect(result.current.gridState).toEqual({
columnState: undefined,
filterModel: undefined,
});
});
it.each(Object.values(Filter))(
'sets and stores column state and filters for %s',
(filter) => {
@@ -30,7 +59,12 @@ describe('useOrderListGridState', () => {
expect(result.current.gridState).toEqual({
columnState: undefined,
filterModel,
filterModel: {
...filterModel,
status: {
value: FilterStatusValue[filter],
},
},
});
const columnState = [{ colId: 'status', width: 200 }];
@@ -43,7 +77,12 @@ describe('useOrderListGridState', () => {
expect(result.current.gridState).toEqual({
columnState,
filterModel,
filterModel: {
...filterModel,
status: {
value: FilterStatusValue[filter],
},
},
});
const storeKeyMap = {
@@ -9,7 +9,6 @@ import type { DataGridStore } from '../../stores/datagrid-store-slice';
import { OrderStatus } from '@vegaprotocol/types';
import { Links } from '../../lib/links';
import { useT } from '../../lib/use-t';
import { GridSettings } from '../grid-settings/grid-settings';
const resolveNoRowsMessage = (
filter: Filter | undefined,
@@ -39,24 +38,6 @@ export const FilterStatusValue = {
[Filter.Rejected]: [OrderStatus.STATUS_REJECTED],
};
export const DefaultFilterModel = {
[Filter.Open]: {
status: {
value: FilterStatusValue[Filter.Open],
},
},
[Filter.Closed]: {
status: {
value: FilterStatusValue[Filter.Closed],
},
},
[Filter.Rejected]: {
status: {
value: FilterStatusValue[Filter.Rejected],
},
},
};
export interface OrderContainerProps {
filter?: Filter;
}
@@ -73,8 +54,7 @@ export const OrdersContainer = ({ filter }: OrderContainerProps) => {
(newState) => {
updateGridState(filter, newState);
},
AUTO_SIZE_COLUMNS,
filter && DefaultFilterModel[filter]
AUTO_SIZE_COLUMNS
);
if (!pubKey) {
@@ -100,7 +80,7 @@ export const OrdersContainer = ({ filter }: OrderContainerProps) => {
};
export const STORAGE_KEY = 'vega_order_list_store';
export const useOrderListStore = create<{
const useOrderListStore = create<{
open: DataGridStore;
closed: DataGridStore;
rejected: DataGridStore;
@@ -169,19 +149,34 @@ export const useOrderListGridState = (filter: Filter | undefined) => {
case Filter.Open: {
return {
columnState: store.open.columnState,
filterModel: store.open.filterModel,
filterModel: {
...store.open.filterModel,
status: {
value: FilterStatusValue[Filter.Open],
},
},
};
}
case Filter.Closed: {
return {
columnState: store.closed.columnState,
filterModel: store.closed.filterModel,
filterModel: {
...store.closed.filterModel,
status: {
value: FilterStatusValue[Filter.Closed],
},
},
};
}
case Filter.Rejected: {
return {
columnState: store.rejected.columnState,
filterModel: store.rejected.filterModel,
filterModel: {
...store.rejected.filterModel,
status: {
value: FilterStatusValue[Filter.Rejected],
},
},
};
}
default: {
@@ -192,14 +187,3 @@ export const useOrderListGridState = (filter: Filter | undefined) => {
return { gridState, updateGridState };
};
export const OrdersSettings = ({ filter }: { filter?: Filter }) => {
const updateGridState = useOrderListStore((state) => state.update);
return (
<GridSettings
updateGridStore={(gridStore: DataGridStore) =>
updateGridState(filter, gridStore)
}
/>
);
};
@@ -1,2 +1 @@
export * from './positions-container';
export * from './positions-settings';
@@ -1,8 +0,0 @@
import { GridSettings } from '../grid-settings/grid-settings';
import { usePositionsStore } from './positions-container';
export const PositionsSettings = () => (
<GridSettings
updateGridStore={usePositionsStore((store) => store.updateGridStore)}
/>
);
@@ -1,3 +1 @@
export * from './stop-orders-container';
export * from './stop-orders-settings';
@@ -35,7 +35,7 @@ export const StopOrdersContainer = () => {
);
};
export const useStopOrdersStore = create<DataGridSlice>()(
const useStopOrdersStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_stop_orders_store',
})
@@ -1,8 +0,0 @@
import { GridSettings } from '../grid-settings/grid-settings';
import { useStopOrdersStore } from './stop-orders-container';
export const StopOrdersSettings = () => (
<GridSettings
updateGridStore={useStopOrdersStore((store) => store.updateGridStore)}
/>
);
@@ -1,2 +1 @@
export * from './trades-container';
export * from './trades-settings';
@@ -17,7 +17,7 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
return <TradesManager marketId={marketId} gridProps={gridStoreCallbacks} />;
};
export const useTradesStore = create<DataGridSlice>()(
const useTradesStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_trades_store',
})
@@ -1,8 +0,0 @@
import { GridSettings } from '../grid-settings/grid-settings';
import { useTradesStore } from './trades-container';
export const TradesSettings = () => (
<GridSettings
updateGridStore={useTradesStore((store) => store.updateGridStore)}
/>
);
@@ -66,17 +66,16 @@ def test_iceberg_open_order(continuous_market, vega: VegaService, page: Page):
page.wait_for_selector(".ag-center-cols-container .ag-row")
expect(
page.locator(".ag-center-cols-container .ag-row [col-id='remaining']").first
page.locator(".ag-center-cols-container .ag-row [col-id='remaining']")
).to_have_text("99")
expect(
page.locator(".ag-center-cols-container .ag-row [col-id='size']").first
page.locator(".ag-center-cols-container .ag-row [col-id='size']")
).to_have_text("-102")
page.pause()
expect(
page.locator(".ag-center-cols-container .ag-row [col-id='type'] ").first
page.locator(".ag-center-cols-container .ag-row [col-id='type'] ")
).to_have_text("Limit (Iceberg)")
expect(
page.locator(".ag-center-cols-container .ag-row [col-id='status']").first
page.locator(".ag-center-cols-container .ag-row [col-id='status']")
).to_have_text("Active")
expect(page.get_by_test_id("price-10100000")).to_be_visible
expect(page.get_by_test_id("ask-vol-10100000")).to_have_text("3")
@@ -15,11 +15,11 @@ def verify_data_grid(page: Page, data_test_id, expected_pattern):
expect(
page.locator(
f'[data-testid^="tab-{data_test_id.lower()}"] >> .ag-center-cols-container .ag-row-first'
).first
)
).to_be_visible()
actual_text = page.locator(
f'[data-testid^="tab-{data_test_id.lower()}"] >> .ag-center-cols-container .ag-row-first'
).first.text_content()
).text_content()
lines = actual_text.strip().split("\n")
for expected, actual in zip(expected_pattern, lines):
# We are using regex so that we can run tests in different timezones.
@@ -65,9 +65,18 @@ export const DateRangeFilter = forwardRef(
useImperativeHandle(ref, () => {
return {
doesFilterPass(params: IDoesFilterPassParams) {
const { column } = props;
const { api, colDef, column, columnApi, context } = props;
const { node } = params;
const rowValue = props.getValue(node, column);
const rowValue = props.valueGetter({
api,
colDef,
column,
columnApi,
context,
data: node.data,
getValue: (field) => node.data[field],
node,
});
if (
value.start &&
rowValue &&
+11 -2
View File
@@ -19,9 +19,18 @@ export const SetFilter = forwardRef(
useImperativeHandle(ref, () => {
return {
doesFilterPass(params: IDoesFilterPassParams) {
const { column } = props;
const { api, colDef, column, columnApi, context } = props;
const { node } = params;
const getValue = props.getValue(node, column);
const getValue = props.valueGetter({
api,
colDef,
column,
columnApi,
context,
data: node.data,
getValue: (field) => node.data[field],
node,
});
return Array.isArray(value)
? value.includes(getValue)
: getValue === value;
@@ -61,7 +61,7 @@ describe('useDataGridEvents', () => {
// column state was not updated, so the default width provided by the
// col def should be set
expect(gridRef?.current?.api.getColumnState()[0].width).toEqual(
expect(gridRef?.current?.columnApi.getColumnState()[0].width).toEqual(
gridProps.columnDefs[0].width
);
// no filters set
@@ -107,57 +107,16 @@ describe('useDataGridEvents', () => {
columnState: [colState],
};
setup(initialState, jest.fn(), undefined);
setup(initialState, jest.fn());
await waitFor(() => {
expect(gridRef?.current?.api.getFilterModel()['id']).toEqual(idFilter);
expect(gridRef?.current?.api.getColumnState()[0]).toEqual(
expect(gridRef?.current?.columnApi.getColumnState()[0]).toEqual(
expect.objectContaining(colState)
);
});
});
it('applies default filter model', async () => {
const idFilter = {
filter: 1,
filterType: 'number',
type: 'equals',
};
setup({}, jest.fn(), undefined, {
id: idFilter,
});
await waitFor(() => {
expect(gridRef?.current?.api.getFilterModel()['id']).toEqual(idFilter);
});
});
it('default filter overwrites stored filter model', async () => {
const idFilter = {
filter: 1,
filterType: 'number',
type: 'equals',
};
setup(
{
filterModel: {
id: { ...idFilter, filter: 2 },
},
},
jest.fn(),
undefined,
{
id: idFilter,
}
);
await waitFor(() => {
expect(gridRef?.current?.api.getFilterModel()['id']).toEqual(idFilter);
});
});
it('ignores events that were not made via the UI', async () => {
const callback = jest.fn();
const initialState = {
@@ -171,7 +130,7 @@ describe('useDataGridEvents', () => {
// Set col width multiple times
await act(async () => {
gridRef?.current?.api.setColumnWidth('id', newWidth);
gridRef?.current?.columnApi.setColumnWidth('id', newWidth);
});
expect(callback).not.toHaveBeenCalled();
@@ -191,14 +150,14 @@ describe('useDataGridEvents', () => {
};
const { rerender } = setup(initialState, callback, ['id']);
if (gridRef?.current?.api) {
jest.spyOn(gridRef?.current?.api, 'autoSizeColumns');
}
jest.spyOn(gridRef?.current?.columnApi, 'autoSizeColumns');
rerender(<TestComponent hookParams={[initialState, callback, ['id']]} />);
act(() => {
gridRef?.current?.api.setGridOption('rowData', [{ id: 'test-id' }]);
gridRef?.current?.api.setRowData([{ id: 'test-id' }]);
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
});
expect(gridRef?.current?.api.autoSizeColumns).toHaveBeenCalledWith(['id']);
expect(gridRef?.current?.columnApi.autoSizeColumns).toHaveBeenCalledWith([
'id',
]);
});
});
+20 -34
View File
@@ -7,9 +7,8 @@ import {
type FirstDataRenderedEvent,
type SortChangedEvent,
type GridReadyEvent,
GridApi,
} from 'ag-grid-community';
import { useCallback, useEffect, useRef } from 'react';
import { useCallback } from 'react';
type State = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -20,25 +19,8 @@ type State = {
export const useDataGridEvents = (
state: State,
callback: (data: State) => void,
autoSizeColumns?: string[],
defaultFilterModel?: State['filterModel']
autoSizeColumns?: string[]
) => {
const apiRef = useRef<GridApi | undefined>();
useEffect(() => {
if (apiRef.current?.isDestroyed()) {
apiRef.current = undefined;
}
if (apiRef.current) {
if (!state.columnState) {
apiRef.current.resetColumnState();
}
if (!state.filterModel) {
apiRef.current.setFilterModel(defaultFilterModel);
}
}
}, [state, defaultFilterModel]);
/**
* Callback for filter events
*/
@@ -57,7 +39,11 @@ export const useDataGridEvents = (
* store callback unnecessarily
*/
const onDebouncedColumnChange = useCallback(
({ api, source, finished }: ColumnResizedEvent | ColumnMovedEvent) => {
({
columnApi,
source,
finished,
}: ColumnResizedEvent | ColumnMovedEvent) => {
if (!finished) return;
// only call back on user interactions, and not events triggered from the api
@@ -71,7 +57,7 @@ export const useDataGridEvents = (
return;
}
const columnState = api.getColumnState();
const columnState = columnApi.getColumnState();
callback({ columnState });
},
@@ -82,8 +68,8 @@ export const useDataGridEvents = (
* Callback for sort and visible events
*/
const onColumnChange = useCallback(
({ api }: SortChangedEvent | ColumnVisibleEvent) => {
const columnState = api.getColumnState();
({ columnApi }: SortChangedEvent | ColumnVisibleEvent) => {
const columnState = columnApi.getColumnState();
callback({ columnState });
},
[callback]
@@ -94,11 +80,11 @@ export const useDataGridEvents = (
* State only applied if found, otherwise columns sized to fit available space
*/
const onGridReady = useCallback(
({ api }: GridReadyEvent) => {
apiRef.current = api;
if (!api) return;
({ api, columnApi }: GridReadyEvent) => {
if (!api || !columnApi) return;
if (state.columnState) {
api.applyColumnState({
columnApi.applyColumnState({
state: state.columnState,
applyOrder: true,
});
@@ -106,18 +92,18 @@ export const useDataGridEvents = (
api.sizeColumnsToFit();
}
if (state.filterModel || defaultFilterModel) {
api.setFilterModel({ ...state.filterModel, ...defaultFilterModel });
if (state.filterModel) {
api.setFilterModel(state.filterModel);
}
},
[state, defaultFilterModel]
[state]
);
const onFirstDataRendered = useCallback(
({ api }: FirstDataRenderedEvent) => {
if (!api) return;
({ columnApi }: FirstDataRenderedEvent) => {
if (!columnApi) return;
if (!state?.columnState && autoSizeColumns?.length) {
api.autoSizeColumns(autoSizeColumns);
columnApi.autoSizeColumns(autoSizeColumns);
}
},
[state, autoSizeColumns]
-1
View File
@@ -234,7 +234,6 @@
"Rejected": "Rejected",
"Required epochs": "Required epochs",
"Required for next tier": "Required for next tier",
"Reset Columns": "Reset Columns",
"Resources": "Resources",
"Rewards": "Rewards",
"Rewards history": "Rewards history",
@@ -135,7 +135,7 @@ describe('MarketInfoPanels', () => {
render(<DataSourceProof dataSourceSpecId={''} {...props} />);
expect(screen.getByText('Internal conditions')).toBeInTheDocument();
const dateFromUnixTimestamp = condition.value
? getDateTimeFormat().format(new Date(parseInt(condition.value)))
? getDateTimeFormat().format(new Date(parseInt(condition.value) * 1000))
: '-';
expect(
screen.getByText(
@@ -1263,7 +1263,9 @@ export const DataSourceProof = ({
{data.sourceType.sourceType?.conditions?.map((condition, i) => {
if (!condition) return null;
const dateFromUnixTimestamp = condition.value
? getDateTimeFormat().format(new Date(parseInt(condition.value)))
? getDateTimeFormat().format(
new Date(parseInt(condition.value) * 1000)
)
: '-';
return (
<p key={i}>
@@ -68,10 +68,13 @@ export const useBlockRising = (skip = false) => {
}
);
const heights = compact([
...results.map((r) => r?.blockHeight),
blockInfo?.result.block.header.height,
]);
const heights = compact([...results.map((r) => r?.blockHeight)]);
// Handles TendermintErrorResponses
if (blockInfo && 'result' in blockInfo) {
heights.push(blockInfo.result.block.header.height);
}
const current = max(heights);
if (current && Number(current) > prev) {
setBlock(Number(current));
+13 -3
View File
@@ -76,16 +76,26 @@ export const useFetch = <T>(
...options,
body: body ? body : options?.body,
});
if (!response.ok) {
data = (await response.json()) as T;
if (!response.ok && !data) {
throw new Error(response.statusText);
}
data = (await response.json()) as T;
// @ts-ignore - 'error' in data
if (data && 'error' in data) {
if (data && data.error) {
// Explicit check for TendermintErrorResponse style error
// @ts-ignore - 'error' in data
if (data.error.data) {
// @ts-ignore - 'error' in data
throw new Error(data.error.data);
}
// @ts-ignore - data.error
throw new Error(data.error);
}
if (cancelRequest.current) return;
dispatch({ type: ActionType.FETCHED, payload: data });
+7 -2
View File
@@ -1,6 +1,11 @@
import { useEnvironment } from '@vegaprotocol/environment';
import { useFetch } from '@vegaprotocol/react-helpers';
import { type TendermintBlockResponse } from '../types';
import type {
TendermintBlockResponse,
TendermintErrorResponse,
} from '../types';
type TendermintResponse = TendermintBlockResponse | TendermintErrorResponse;
export const useBlockInfo = (blockHeight?: number, canFetch = true) => {
const { TENDERMINT_URL } = useEnvironment();
@@ -10,7 +15,7 @@ export const useBlockInfo = (blockHeight?: number, canFetch = true) => {
TENDERMINT_URL && blockHeight && !isNaN(blockHeight) && canFetch
);
const { state, refetch } = useFetch<TendermintBlockResponse>(
const { state, refetch } = useFetch<TendermintResponse>(
url,
{ cache: 'force-cache' },
canFetchData
+11 -1
View File
@@ -7,6 +7,16 @@ export type TendermintBlockResponse = {
};
};
export type TendermintErrorResponse = {
jsonrpc: string;
id: number;
error: {
code: number;
message: string;
data: string;
};
};
type Id = {
hash: string;
parts: {
@@ -34,7 +44,7 @@ type Header = {
proposer_address: string;
};
type Block = {
export type Block = {
header: Header;
data: {
txs: string[];
+10 -34
View File
@@ -7,8 +7,6 @@ import {
import classNames from 'classnames';
import type { ReactElement, ReactNode } from 'react';
import { Children, isValidElement, useRef, useState } from 'react';
import { VegaIcon } from '../icon/vega-icons/vega-icon';
import { VegaIconNames } from '../icon/vega-icons/vega-icon-record';
export interface TabsProps extends TabsPrimitive.TabsProps {
children: (ReactElement<TabProps> | null)[];
}
@@ -20,10 +18,12 @@ export const Tabs = ({
onValueChange,
...props
}: TabsProps) => {
const [settingsOpened, setSettingsOpened] = useState(false);
const [activeTab, setActiveTab] = useState<string | undefined>(
() => value || defaultValue || children.find((v) => v)?.props.id
);
const [activeTab, setActiveTab] = useState<string | undefined>(() => {
if (defaultValue) {
return defaultValue;
}
return children.find((v) => v)?.props.id;
});
// Bunch of refs in order to detect wrapping in side the tabs so that we
// can apply a bg color
@@ -42,14 +42,8 @@ export const Tabs = ({
<TabsPrimitive.Root
{...props}
value={value || activeTab}
onValueChange={(value) => {
setSettingsOpened(false);
setActiveTab(value);
if (onValueChange) {
onValueChange(value);
}
}}
className="h-full grid grid-rows-[min-content_1fr] relative"
onValueChange={onValueChange || setActiveTab}
className="h-full grid grid-rows-[min-content_1fr]"
>
<div
ref={wrapperRef}
@@ -93,7 +87,7 @@ export const Tabs = ({
</TabsPrimitive.List>
<div
ref={menuRef}
className={classNames('flex justify-end flex-1 p-1', {
className={classNames('flex-1 p-1', {
'bg-vega-clight-700 dark:bg-vega-cdark-700': wrapped,
})}
>
@@ -107,24 +101,12 @@ export const Tabs = ({
})}
>
{child.props.menu}
{isValidElement(child.props.settings) && (
<button
className="ml-1 flex items-center justify-center h-6 w-6"
onClick={() => setSettingsOpened((v) => !v)}
>
{settingsOpened ? (
<VegaIcon name={VegaIconNames.CROSS} size={16} />
) : (
<VegaIcon name={VegaIconNames.COG} size={16} />
)}
</button>
)}
</TabsPrimitive.Content>
);
})}
</div>
</div>
<div className="relative h-full overflow-auto">
<div className="h-full overflow-auto">
{Children.map(children, (child) => {
if (!isValidElement(child) || child.props.hidden) return null;
return (
@@ -136,11 +118,6 @@ export const Tabs = ({
data-testid={`tab-${child.props.id}`}
>
{child.props.children}
{isValidElement(child.props.settings) && settingsOpened && (
<div className="absolute right-0 top-0 bottom-0 p-2 w-[280px] max-w-full border-l border-default bg-vega-clight-700 dark:bg-vega-cdark-700 z-10">
{child.props.settings}
</div>
)}
</TabsPrimitive.Content>
);
})}
@@ -157,7 +134,6 @@ interface TabProps {
hidden?: boolean;
overflowHidden?: boolean;
menu?: ReactNode;
settings?: ReactNode;
}
export const Tab = ({ children, ...props }: TabProps) => {
+2 -2
View File
@@ -49,8 +49,8 @@
"@web3-react/metamask": "^8.1.2-beta.0",
"@web3-react/walletconnect": "8.1.3-beta.0",
"@web3-react/walletconnect-v2": "^8.1.3-beta.0",
"ag-grid-community": "^31.0.1",
"ag-grid-react": "^31.0.1",
"ag-grid-community": "^29.3.5",
"ag-grid-react": "^29.3.5",
"allotment": "1.19.2",
"alpha-lyrae": "vegaprotocol/alpha-lyrae",
"apollo-link-timeout": "^4.0.0",
+8 -9
View File
@@ -8609,17 +8609,16 @@ aes-js@^3.1.2:
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a"
integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==
ag-grid-community@^31.0.1, ag-grid-community@~31.0.1:
version "31.0.1"
resolved "https://registry.yarnpkg.com/ag-grid-community/-/ag-grid-community-31.0.1.tgz#26022b29a7b90a0515076837d630ac9cd24cf28d"
integrity sha512-RZQlW1DTOJHsUR/tnbnTJQKgAnDlHi05YYyTe5AgNor/1TlX1hoYdcqrGsJjvcHQgTjeEgzWOL0yf+KcqXZzxg==
ag-grid-community@^29.3.5:
version "29.3.5"
resolved "https://registry.yarnpkg.com/ag-grid-community/-/ag-grid-community-29.3.5.tgz#16897896d10fa3ecac79279aad50d3aaa17c5f33"
integrity sha512-LxUo21f2/CH31ACEs1C7Q/ggGGI1fQPSTB4aY5OThmM+lBkygZ7QszBE8jpfgWOIjvjdtcdIeQbmbjkHeMsA7A==
ag-grid-react@^31.0.1:
version "31.0.1"
resolved "https://registry.yarnpkg.com/ag-grid-react/-/ag-grid-react-31.0.1.tgz#c7e3cf029ea1b97ab7f1d5134c8bb0086b4d2aac"
integrity sha512-9nmYPsgH1YUDUDOTiyaFsysoNAx/y72ovFJKuOffZC1V7OrQMadyP6DbqGFWCqzzoLJOY7azOr51dDQzAIXLpw==
ag-grid-react@^29.3.5:
version "29.3.5"
resolved "https://registry.yarnpkg.com/ag-grid-react/-/ag-grid-react-29.3.5.tgz#0eae8934d372c7751e98789542fc663aee0ad6ad"
integrity sha512-Eg0GJ8hEBuxdVaN5g+qITOzhw0MGL9avL0Oaajr+p7QRtq2pIFHLZSknWsCBzUTjidiu75WZMKwlZjtGEuafdQ==
dependencies:
ag-grid-community "~31.0.1"
prop-types "^15.8.1"
agent-base@5: