refactor(trading): pane stores for grid state (#4119)
This commit is contained in:
parent
77a391448b
commit
c3e39b6e15
@ -1,5 +1,4 @@
|
|||||||
import { checkSorting } from '@vegaprotocol/cypress';
|
import { checkSorting, aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
|
||||||
import { marketsDataQuery } from '@vegaprotocol/mock';
|
import { marketsDataQuery } from '@vegaprotocol/mock';
|
||||||
import { positionsQuery } from '@vegaprotocol/mock';
|
import { positionsQuery } from '@vegaprotocol/mock';
|
||||||
|
|
||||||
@ -15,13 +14,12 @@ const toastContent = 'toast-content';
|
|||||||
const tooltipContent = 'tooltip-content';
|
const tooltipContent = 'tooltip-content';
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
|
describe('positions', { tags: '@smoke', testIsolation: true }, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.mockTradingPage();
|
cy.mockTradingPage();
|
||||||
cy.mockSubscription();
|
cy.mockSubscription();
|
||||||
cy.setVegaWallet();
|
cy.setVegaWallet();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('positions', { tags: '@smoke', testIsolation: true }, () => {
|
|
||||||
it('renders positions on trading page', () => {
|
it('renders positions on trading page', () => {
|
||||||
visitAndClickPositions();
|
visitAndClickPositions();
|
||||||
// 7004-POSI-001
|
// 7004-POSI-001
|
||||||
@ -64,7 +62,14 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.mockTradingPage();
|
||||||
|
cy.mockSubscription();
|
||||||
|
cy.setVegaWallet();
|
||||||
|
});
|
||||||
|
|
||||||
it('rows should be displayed despite errors', () => {
|
it('rows should be displayed despite errors', () => {
|
||||||
const errors = [
|
const errors = [
|
||||||
{
|
{
|
||||||
@ -164,8 +169,9 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// let elementWidth: number;
|
||||||
|
|
||||||
it('Resize column', () => {
|
it('Resize column', () => {
|
||||||
let elementWidth: number;
|
|
||||||
visitAndClickPositions();
|
visitAndClickPositions();
|
||||||
cy.get('.ag-overlay-loading-wrapper').should('not.be.visible');
|
cy.get('.ag-overlay-loading-wrapper').should('not.be.visible');
|
||||||
cy.get('.ag-header-container').within(() => {
|
cy.get('.ag-header-container').within(() => {
|
||||||
@ -180,29 +186,33 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
|||||||
cy.get(`[col-id="marketName"]`)
|
cy.get(`[col-id="marketName"]`)
|
||||||
.invoke('width')
|
.invoke('width')
|
||||||
.should('be.greaterThan', 250);
|
.should('be.greaterThan', 250);
|
||||||
cy.get(`[col-id="marketName"]`)
|
|
||||||
.invoke('width')
|
|
||||||
.then((width) => {
|
|
||||||
elementWidth = width as number;
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
let localStorageCopy: Record<string, string>;
|
|
||||||
cy.window().then((win) => {
|
|
||||||
localStorageCopy = { ...win.localStorage };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.reload();
|
// This test depends on the previous one
|
||||||
|
it('Has persisted column widths', () => {
|
||||||
|
const width = 400;
|
||||||
|
|
||||||
cy.window().then((win) => {
|
cy.window().then((win) => {
|
||||||
Object.keys(localStorageCopy).forEach((key) => {
|
win.localStorage.setItem(
|
||||||
win.localStorage.setItem(key, localStorageCopy[key]);
|
'vega_positions_store',
|
||||||
});
|
JSON.stringify({
|
||||||
|
state: {
|
||||||
|
gridStore: {
|
||||||
|
columnState: [{ colId: 'marketName', width }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
visitAndClickPositions();
|
||||||
|
|
||||||
// 7004-POSI-012
|
// 7004-POSI-012
|
||||||
cy.get('[col-id="marketName"]')
|
cy.get('.ag-center-cols-container .ag-row')
|
||||||
.invoke('width')
|
.first()
|
||||||
.should('equal', elementWidth);
|
.find('[col-id="marketName"]')
|
||||||
});
|
.invoke('outerWidth')
|
||||||
|
.should('equal', width);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Scroll horizontally', () => {
|
it('Scroll horizontally', () => {
|
||||||
@ -291,6 +301,7 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
|||||||
cy.getByTestId(dialogContent).should('be.visible');
|
cy.getByTestId(dialogContent).should('be.visible');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function validatePositionsDisplayed(multiKey = false) {
|
function validatePositionsDisplayed(multiKey = false) {
|
||||||
cy.getByTestId('tab-positions').should('be.visible');
|
cy.getByTestId('tab-positions').should('be.visible');
|
||||||
cy.getByTestId('tab-positions')
|
cy.getByTestId('tab-positions')
|
||||||
@ -326,6 +337,7 @@ function validatePositionsDisplayed(multiKey = false) {
|
|||||||
|
|
||||||
cy.getByTestId('close-position').should('be.visible').and('have.length', 3);
|
cy.getByTestId('close-position').should('be.visible').and('have.length', 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertPNLColor(
|
function assertPNLColor(
|
||||||
pnlSelector: string,
|
pnlSelector: string,
|
||||||
positiveClass: string,
|
positiveClass: string,
|
||||||
@ -347,6 +359,7 @@ function assertPNLColor(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function visitAndClickPositions() {
|
function visitAndClickPositions() {
|
||||||
cy.visit('/#/markets/market-0');
|
cy.visit('/#/markets/market-0');
|
||||||
cy.getByTestId(positions).click();
|
cy.getByTestId(positions).click();
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
matchFilter,
|
matchFilter,
|
||||||
liquidityProvisionsDataProvider,
|
|
||||||
LiquidityTable,
|
|
||||||
lpAggregatedDataProvider,
|
lpAggregatedDataProvider,
|
||||||
useCheckLiquidityStatus,
|
useCheckLiquidityStatus,
|
||||||
} from '@vegaprotocol/liquidity';
|
} from '@vegaprotocol/liquidity';
|
||||||
@ -24,18 +22,16 @@ import {
|
|||||||
ExternalLink,
|
ExternalLink,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { memo, useEffect, useRef, useState } from 'react';
|
import { memo, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { Header, HeaderStat, HeaderTitle } from '../../components/header';
|
import { Header, HeaderStat, HeaderTitle } from '../../components/header';
|
||||||
|
|
||||||
import type { AgGridReact } from 'ag-grid-react';
|
|
||||||
|
|
||||||
import type { Filter } from '@vegaprotocol/liquidity';
|
|
||||||
import { Link, useParams } from 'react-router-dom';
|
import { Link, useParams } from 'react-router-dom';
|
||||||
import { Links, Routes } from '../../pages/client-router';
|
import { Links, Routes } from '../../pages/client-router';
|
||||||
|
|
||||||
import { useMarket, useStaticMarketData } from '@vegaprotocol/markets';
|
import { useMarket, useStaticMarketData } from '@vegaprotocol/markets';
|
||||||
import { DocsLinks } from '@vegaprotocol/environment';
|
import { DocsLinks } from '@vegaprotocol/environment';
|
||||||
|
import { LiquidityContainer } from '../../components/liquidity-container';
|
||||||
|
|
||||||
const enum LiquidityTabs {
|
const enum LiquidityTabs {
|
||||||
Active = 'active',
|
Active = 'active',
|
||||||
@ -49,65 +45,6 @@ export const Liquidity = () => {
|
|||||||
return <LiquidityViewContainer marketId={marketId} />;
|
return <LiquidityViewContainer marketId={marketId} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useReloadLiquidityData = (marketId: string | undefined) => {
|
|
||||||
const { reload } = useDataProvider({
|
|
||||||
dataProvider: liquidityProvisionsDataProvider,
|
|
||||||
variables: { marketId: marketId || '' },
|
|
||||||
update: () => true,
|
|
||||||
skip: !marketId,
|
|
||||||
});
|
|
||||||
useEffect(() => {
|
|
||||||
const interval = setInterval(reload, 30000);
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, [reload]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const LiquidityContainer = ({
|
|
||||||
marketId,
|
|
||||||
filter,
|
|
||||||
}: {
|
|
||||||
marketId: string | undefined;
|
|
||||||
filter?: Filter;
|
|
||||||
}) => {
|
|
||||||
const gridRef = useRef<AgGridReact | null>(null);
|
|
||||||
const { data: market } = useMarket(marketId);
|
|
||||||
|
|
||||||
// To be removed when liquidityProvision subscriptions are working
|
|
||||||
useReloadLiquidityData(marketId);
|
|
||||||
|
|
||||||
const { data, error } = useDataProvider({
|
|
||||||
dataProvider: lpAggregatedDataProvider,
|
|
||||||
variables: { marketId: marketId || '', filter },
|
|
||||||
skip: !marketId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const assetDecimalPlaces =
|
|
||||||
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
|
|
||||||
const quantum =
|
|
||||||
market?.tradableInstrument.instrument.product.settlementAsset.quantum || 0;
|
|
||||||
const symbol =
|
|
||||||
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
|
|
||||||
|
|
||||||
const { params } = useNetworkParams([
|
|
||||||
NetworkParams.market_liquidity_stakeToCcyVolume,
|
|
||||||
]);
|
|
||||||
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-full relative">
|
|
||||||
<LiquidityTable
|
|
||||||
ref={gridRef}
|
|
||||||
rowData={data}
|
|
||||||
symbol={symbol}
|
|
||||||
assetDecimalPlaces={assetDecimalPlaces}
|
|
||||||
quantum={quantum}
|
|
||||||
stakeToCcyVolume={stakeToCcyVolume}
|
|
||||||
overlayNoRowsTemplate={error ? error.message : t('No data')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
|
const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
|
||||||
const { data: market } = useMarket(marketId);
|
const { data: market } = useMarket(marketId);
|
||||||
const { data: marketData } = useStaticMarketData(marketId);
|
const { data: marketData } = useStaticMarketData(marketId);
|
||||||
|
@ -19,10 +19,7 @@ import {
|
|||||||
VegaIcon,
|
VegaIcon,
|
||||||
VegaIconNames,
|
VegaIconNames,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import {
|
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||||
useMarketClickHandler,
|
|
||||||
useMarketLiquidityClickHandler,
|
|
||||||
} from '../../lib/hooks/use-market-click-handler';
|
|
||||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||||
import { HeaderTitle } from '../../components/header';
|
import { HeaderTitle } from '../../components/header';
|
||||||
import {
|
import {
|
||||||
@ -49,7 +46,6 @@ const MarketBottomPanel = memo(
|
|||||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'bottom' });
|
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'bottom' });
|
||||||
const { screenSize } = useScreenDimensions();
|
const { screenSize } = useScreenDimensions();
|
||||||
const onMarketClick = useMarketClickHandler(true);
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
|
||||||
|
|
||||||
return 'xxxl' === screenSize ? (
|
return 'xxxl' === screenSize ? (
|
||||||
<ResizableGrid
|
<ResizableGrid
|
||||||
@ -69,10 +65,6 @@ const MarketBottomPanel = memo(
|
|||||||
<TradingViews.orders.component
|
<TradingViews.orders.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
filter={Filter.Open}
|
filter={Filter.Open}
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
|
||||||
enforceBottomPlaceholder
|
|
||||||
storeKey="marketOpenOrders"
|
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
@ -81,10 +73,6 @@ const MarketBottomPanel = memo(
|
|||||||
<TradingViews.orders.component
|
<TradingViews.orders.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
filter={Filter.Closed}
|
filter={Filter.Closed}
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
|
||||||
enforceBottomPlaceholder
|
|
||||||
storeKey="marketClosedOrders"
|
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
@ -93,22 +81,12 @@ const MarketBottomPanel = memo(
|
|||||||
<TradingViews.orders.component
|
<TradingViews.orders.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
filter={Filter.Rejected}
|
filter={Filter.Rejected}
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
|
||||||
enforceBottomPlaceholder
|
|
||||||
storeKey="marketRejectOrders"
|
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="orders" name={t('All')}>
|
<Tab id="orders" name={t('All')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<TradingViews.orders.component
|
<TradingViews.orders.component marketId={marketId} />
|
||||||
marketId={marketId}
|
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
|
||||||
enforceBottomPlaceholder
|
|
||||||
storeKey="marketAllOrders"
|
|
||||||
/>
|
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="fills" name={t('Fills')}>
|
<Tab id="fills" name={t('Fills')}>
|
||||||
@ -116,7 +94,6 @@ const MarketBottomPanel = memo(
|
|||||||
<TradingViews.fills.component
|
<TradingViews.fills.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
storeKey="marketFills"
|
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
@ -134,8 +111,6 @@ const MarketBottomPanel = memo(
|
|||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<TradingViews.positions.component
|
<TradingViews.positions.component
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
noBottomPlaceholder
|
|
||||||
storeKey="marketPositions"
|
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
@ -145,7 +120,6 @@ const MarketBottomPanel = memo(
|
|||||||
pinnedAsset={pinnedAsset}
|
pinnedAsset={pinnedAsset}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
hideButtons
|
hideButtons
|
||||||
storeKey="marketCollateral"
|
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
@ -158,10 +132,7 @@ const MarketBottomPanel = memo(
|
|||||||
<Tabs storageKey="console-trade-grid-bottom">
|
<Tabs storageKey="console-trade-grid-bottom">
|
||||||
<Tab id="positions" name={t('Positions')}>
|
<Tab id="positions" name={t('Positions')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<TradingViews.positions.component
|
<TradingViews.positions.component onMarketClick={onMarketClick} />
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
storeKey="marketPositions"
|
|
||||||
/>
|
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="open-orders" name={t('Open')}>
|
<Tab id="open-orders" name={t('Open')}>
|
||||||
@ -169,10 +140,6 @@ const MarketBottomPanel = memo(
|
|||||||
<TradingViews.orders.component
|
<TradingViews.orders.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
filter={Filter.Open}
|
filter={Filter.Open}
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
|
||||||
enforceBottomPlaceholder
|
|
||||||
storeKey="marketOpenOrders"
|
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
@ -181,10 +148,6 @@ const MarketBottomPanel = memo(
|
|||||||
<TradingViews.orders.component
|
<TradingViews.orders.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
filter={Filter.Closed}
|
filter={Filter.Closed}
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
|
||||||
enforceBottomPlaceholder
|
|
||||||
storeKey="marketClosedOrders"
|
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
@ -193,22 +156,12 @@ const MarketBottomPanel = memo(
|
|||||||
<TradingViews.orders.component
|
<TradingViews.orders.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
filter={Filter.Rejected}
|
filter={Filter.Rejected}
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
|
||||||
enforceBottomPlaceholder
|
|
||||||
storeKey="marketRejectedOrders"
|
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="orders" name={t('All')}>
|
<Tab id="orders" name={t('All')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<TradingViews.orders.component
|
<TradingViews.orders.component marketId={marketId} />
|
||||||
marketId={marketId}
|
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
|
||||||
enforceBottomPlaceholder
|
|
||||||
storeKey="marketAllOrders"
|
|
||||||
/>
|
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="fills" name={t('Fills')}>
|
<Tab id="fills" name={t('Fills')}>
|
||||||
@ -216,7 +169,6 @@ const MarketBottomPanel = memo(
|
|||||||
<TradingViews.fills.component
|
<TradingViews.fills.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
storeKey="marketFills"
|
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
@ -226,7 +178,6 @@ const MarketBottomPanel = memo(
|
|||||||
pinnedAsset={pinnedAsset}
|
pinnedAsset={pinnedAsset}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
hideButtons
|
hideButtons
|
||||||
storeKey="marketCollateral"
|
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
|
import type { ComponentProps } from 'react';
|
||||||
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
|
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
|
||||||
import { MarketInfoAccordionContainer } from '@vegaprotocol/markets';
|
import { MarketInfoAccordionContainer } from '@vegaprotocol/markets';
|
||||||
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
|
||||||
import { OrderListContainer, Filter } from '@vegaprotocol/orders';
|
|
||||||
import type { OrderListContainerProps } from '@vegaprotocol/orders';
|
|
||||||
import { FillsContainer } from '@vegaprotocol/fills';
|
|
||||||
import { PositionsContainer } from '@vegaprotocol/positions';
|
|
||||||
import { TradesContainer } from '@vegaprotocol/trades';
|
import { TradesContainer } from '@vegaprotocol/trades';
|
||||||
import type { ComponentProps } from 'react';
|
|
||||||
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
||||||
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
|
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
||||||
import { AccountsContainer } from '../../components/accounts-container';
|
import { Filter } from '@vegaprotocol/orders';
|
||||||
import { NO_MARKET } from './constants';
|
import { NO_MARKET } from './constants';
|
||||||
import { LiquidityContainer } from '../liquidity/liquidity';
|
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 type { OrderContainerProps } from '../../components/orders-container';
|
||||||
|
import { OrdersContainer } from '../../components/orders-container';
|
||||||
|
|
||||||
type MarketDependantView =
|
type MarketDependantView =
|
||||||
| typeof CandlesChartContainer
|
| typeof CandlesChartContainer
|
||||||
@ -65,25 +66,25 @@ export const TradingViews = {
|
|||||||
positions: { label: 'Positions', component: PositionsContainer },
|
positions: { label: 'Positions', component: PositionsContainer },
|
||||||
activeOrders: {
|
activeOrders: {
|
||||||
label: 'Active',
|
label: 'Active',
|
||||||
component: (props: OrderListContainerProps) => (
|
component: (props: OrderContainerProps) => (
|
||||||
<OrderListContainer {...props} filter={Filter.Open} />
|
<OrdersContainer {...props} filter={Filter.Open} />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
closedOrders: {
|
closedOrders: {
|
||||||
label: 'Closed',
|
label: 'Closed',
|
||||||
component: (props: OrderListContainerProps) => (
|
component: (props: OrderContainerProps) => (
|
||||||
<OrderListContainer {...props} filter={Filter.Closed} />
|
<OrdersContainer {...props} filter={Filter.Closed} />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
rejectedOrders: {
|
rejectedOrders: {
|
||||||
label: 'Rejected',
|
label: 'Rejected',
|
||||||
component: (props: OrderListContainerProps) => (
|
component: (props: OrderContainerProps) => (
|
||||||
<OrderListContainer {...props} filter={Filter.Rejected} />
|
<OrdersContainer {...props} filter={Filter.Rejected} />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
orders: {
|
orders: {
|
||||||
label: 'All',
|
label: 'All',
|
||||||
component: OrderListContainer,
|
component: OrdersContainer,
|
||||||
},
|
},
|
||||||
collateral: { label: 'Collateral', component: AccountsContainer },
|
collateral: { label: 'Collateral', component: AccountsContainer },
|
||||||
fills: { label: 'Fills', component: FillsContainer },
|
fills: { label: 'Fills', component: FillsContainer },
|
||||||
|
@ -313,7 +313,6 @@ const ClosedMarketsDataGrid = ({
|
|||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
}}
|
}}
|
||||||
overlayNoRowsTemplate={error ? error.message : t('No markets')}
|
overlayNoRowsTemplate={error ? error.message : t('No markets')}
|
||||||
storeKey="closedMarkets"
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,6 @@ import { Button } from '@vegaprotocol/ui-toolkit';
|
|||||||
import { useDepositDialog, DepositsTable } from '@vegaprotocol/deposits';
|
import { useDepositDialog, DepositsTable } from '@vegaprotocol/deposits';
|
||||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
|
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
@ -17,17 +16,13 @@ export const DepositsContainer = () => {
|
|||||||
skip: !pubKey,
|
skip: !pubKey,
|
||||||
});
|
});
|
||||||
const openDepositDialog = useDepositDialog((state) => state.open);
|
const openDepositDialog = useDepositDialog((state) => state.open);
|
||||||
const bottomPlaceholderProps = useBottomPlaceholder({ gridRef });
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
<div className="h-full relative">
|
|
||||||
<DepositsTable
|
<DepositsTable
|
||||||
rowData={data}
|
rowData={data}
|
||||||
ref={gridRef}
|
ref={gridRef}
|
||||||
{...bottomPlaceholderProps}
|
|
||||||
overlayNoRowsTemplate={error ? error.message : t('No deposits')}
|
overlayNoRowsTemplate={error ? error.message : t('No deposits')}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
{!isReadOnly && (
|
{!isReadOnly && (
|
||||||
<div className="h-auto flex justify-end px-[11px] py-2 bottom-0 right-3 absolute dark:bg-black/75 bg-white/75 rounded">
|
<div className="h-auto flex justify-end px-[11px] py-2 bottom-0 right-3 absolute dark:bg-black/75 bg-white/75 rounded">
|
||||||
<Button
|
<Button
|
||||||
|
@ -1,29 +1,26 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import { LayoutPriority } from 'allotment';
|
||||||
import { titlefy } from '@vegaprotocol/utils';
|
import { titlefy } from '@vegaprotocol/utils';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { PositionsContainer } from '@vegaprotocol/positions';
|
import { useIncompleteWithdrawals } from '@vegaprotocol/withdraws';
|
||||||
import { OrderListContainer } from '@vegaprotocol/orders';
|
|
||||||
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { WithdrawalsContainer } from './withdrawals-container';
|
|
||||||
import { FillsContainer } from '@vegaprotocol/fills';
|
|
||||||
import type { ReactNode } from 'react';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { usePaneLayout } from '@vegaprotocol/react-helpers';
|
import { usePaneLayout } from '@vegaprotocol/react-helpers';
|
||||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
|
||||||
import { DepositsContainer } from './deposits-container';
|
|
||||||
import { LayoutPriority } from 'allotment';
|
|
||||||
import { usePageTitleStore } from '../../stores';
|
import { usePageTitleStore } from '../../stores';
|
||||||
import { LedgerContainer } from '@vegaprotocol/ledger';
|
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||||
import { AccountsContainer } from '../../components/accounts-container';
|
import { AccountsContainer } from '../../components/accounts-container';
|
||||||
|
import { DepositsContainer } from './deposits-container';
|
||||||
|
import { FillsContainer } from '../../components/fills-container';
|
||||||
|
import { PositionsContainer } from '../../components/positions-container';
|
||||||
|
import { WithdrawalsContainer } from './withdrawals-container';
|
||||||
|
import { OrdersContainer } from '../../components/orders-container';
|
||||||
|
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||||
|
import { LedgerContainer } from '../../components/ledger-container';
|
||||||
import { AccountHistoryContainer } from './account-history-container';
|
import { AccountHistoryContainer } from './account-history-container';
|
||||||
import {
|
|
||||||
useMarketClickHandler,
|
|
||||||
useMarketLiquidityClickHandler,
|
|
||||||
} from '../../lib/hooks/use-market-click-handler';
|
|
||||||
import {
|
import {
|
||||||
ResizableGrid,
|
ResizableGrid,
|
||||||
ResizableGridPanel,
|
ResizableGridPanel,
|
||||||
} from '../../components/resizable-grid';
|
} from '../../components/resizable-grid';
|
||||||
import { useIncompleteWithdrawals } from '@vegaprotocol/withdraws';
|
|
||||||
|
|
||||||
const WithdrawalsIndicator = () => {
|
const WithdrawalsIndicator = () => {
|
||||||
const { ready } = useIncompleteWithdrawals();
|
const { ready } = useIncompleteWithdrawals();
|
||||||
@ -47,7 +44,6 @@ export const Portfolio = () => {
|
|||||||
}, [updateTitle]);
|
}, [updateTitle]);
|
||||||
|
|
||||||
const onMarketClick = useMarketClickHandler(true);
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
|
||||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
|
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
|
||||||
const wrapperClasses = 'h-full max-h-full flex flex-col';
|
const wrapperClasses = 'h-full max-h-full flex flex-col';
|
||||||
return (
|
return (
|
||||||
@ -63,29 +59,17 @@ export const Portfolio = () => {
|
|||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="positions" name={t('Positions')}>
|
<Tab id="positions" name={t('Positions')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<PositionsContainer
|
<PositionsContainer onMarketClick={onMarketClick} allKeys />
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
noBottomPlaceholder
|
|
||||||
storeKey="portfolioPositions"
|
|
||||||
allKeys
|
|
||||||
/>
|
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="orders" name={t('Orders')}>
|
<Tab id="orders" name={t('Orders')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<OrderListContainer
|
<OrdersContainer />
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
|
||||||
storeKey="portfolioOrders"
|
|
||||||
/>
|
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="fills" name={t('Fills')}>
|
<Tab id="fills" name={t('Fills')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<FillsContainer
|
<FillsContainer onMarketClick={onMarketClick} />
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
storeKey="portfolioFills"
|
|
||||||
/>
|
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="ledger-entries" name={t('Ledger entries')}>
|
<Tab id="ledger-entries" name={t('Ledger entries')}>
|
||||||
@ -105,10 +89,7 @@ export const Portfolio = () => {
|
|||||||
<Tabs storageKey="console-portfolio-bottom">
|
<Tabs storageKey="console-portfolio-bottom">
|
||||||
<Tab id="collateral" name={t('Collateral')}>
|
<Tab id="collateral" name={t('Collateral')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<AccountsContainer
|
<AccountsContainer />
|
||||||
storeKey="portfolioCollateral"
|
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
/>
|
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="deposits" name={t('Deposits')}>
|
<Tab id="deposits" name={t('Deposits')}>
|
||||||
|
@ -8,16 +8,19 @@ import { useVegaWallet } from '@vegaprotocol/wallet';
|
|||||||
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
||||||
import { AccountManager, useTransferDialog } from '@vegaprotocol/accounts';
|
import { AccountManager, useTransferDialog } from '@vegaprotocol/accounts';
|
||||||
import { useDepositDialog } from '@vegaprotocol/deposits';
|
import { useDepositDialog } from '@vegaprotocol/deposits';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
|
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
|
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
|
|
||||||
export const AccountsContainer = ({
|
export const AccountsContainer = ({
|
||||||
pinnedAsset,
|
pinnedAsset,
|
||||||
hideButtons,
|
hideButtons,
|
||||||
storeKey,
|
|
||||||
onMarketClick,
|
onMarketClick,
|
||||||
}: {
|
}: {
|
||||||
pinnedAsset?: PinnedAsset;
|
pinnedAsset?: PinnedAsset;
|
||||||
hideButtons?: boolean;
|
hideButtons?: boolean;
|
||||||
storeKey?: string;
|
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { pubKey, isReadOnly } = useVegaWallet();
|
const { pubKey, isReadOnly } = useVegaWallet();
|
||||||
@ -26,6 +29,12 @@ export const AccountsContainer = ({
|
|||||||
const openDepositDialog = useDepositDialog((store) => store.open);
|
const openDepositDialog = useDepositDialog((store) => store.open);
|
||||||
const openTransferDialog = useTransferDialog((store) => store.open);
|
const openTransferDialog = useTransferDialog((store) => store.open);
|
||||||
|
|
||||||
|
const gridStore = useAccountStore((store) => store.gridStore);
|
||||||
|
const updateGridStore = useAccountStore((store) => store.updateGridStore);
|
||||||
|
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
|
||||||
|
updateGridStore(colState);
|
||||||
|
});
|
||||||
|
|
||||||
const onClickAsset = useCallback(
|
const onClickAsset = useCallback(
|
||||||
(assetId?: string) => {
|
(assetId?: string) => {
|
||||||
assetId && openAssetDetailsDialog(assetId);
|
assetId && openAssetDetailsDialog(assetId);
|
||||||
@ -51,7 +60,7 @@ export const AccountsContainer = ({
|
|||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
pinnedAsset={pinnedAsset}
|
pinnedAsset={pinnedAsset}
|
||||||
storeKey={storeKey}
|
gridProps={gridStoreCallbacks}
|
||||||
/>
|
/>
|
||||||
{!isReadOnly && !hideButtons && (
|
{!isReadOnly && !hideButtons && (
|
||||||
<div className="flex gap-2 justify-end p-2 px-[11px] absolute lg:fixed bottom-0 right-3 dark:bg-black/75 bg-white/75 rounded">
|
<div className="flex gap-2 justify-end p-2 px-[11px] absolute lg:fixed bottom-0 right-3 dark:bg-black/75 bg-white/75 rounded">
|
||||||
@ -75,3 +84,9 @@ export const AccountsContainer = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const useAccountStore = create<DataGridSlice>()(
|
||||||
|
persist(createDataGridSlice, {
|
||||||
|
name: 'vega_accounts_store',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
49
apps/trading/components/fills-container/fills-container.tsx
Normal file
49
apps/trading/components/fills-container/fills-container.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
|
import { FillsManager } from '@vegaprotocol/fills';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
|
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
|
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
|
|
||||||
|
export const FillsContainer = ({
|
||||||
|
marketId,
|
||||||
|
onMarketClick,
|
||||||
|
}: {
|
||||||
|
marketId?: string;
|
||||||
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
|
}) => {
|
||||||
|
const { pubKey } = useVegaWallet();
|
||||||
|
|
||||||
|
const gridStore = useFillsStore((store) => store.gridStore);
|
||||||
|
const updateGridStore = useFillsStore((store) => store.updateGridStore);
|
||||||
|
|
||||||
|
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
|
||||||
|
updateGridStore(colState);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!pubKey) {
|
||||||
|
return (
|
||||||
|
<Splash>
|
||||||
|
<p>{t('Please connect Vega wallet')}</p>
|
||||||
|
</Splash>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FillsManager
|
||||||
|
partyId={pubKey}
|
||||||
|
marketId={marketId}
|
||||||
|
onMarketClick={onMarketClick}
|
||||||
|
gridProps={gridStoreCallbacks}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const useFillsStore = create<DataGridSlice>()(
|
||||||
|
persist(createDataGridSlice, {
|
||||||
|
name: 'vega_fills_store',
|
||||||
|
})
|
||||||
|
);
|
1
apps/trading/components/fills-container/index.ts
Normal file
1
apps/trading/components/fills-container/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './fills-container';
|
1
apps/trading/components/ledger-container/index.ts
Normal file
1
apps/trading/components/ledger-container/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './ledger-container';
|
@ -0,0 +1,36 @@
|
|||||||
|
import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { LedgerManager } from '@vegaprotocol/ledger';
|
||||||
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
|
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
|
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
|
||||||
|
export const LedgerContainer = () => {
|
||||||
|
const { pubKey } = useVegaWallet();
|
||||||
|
|
||||||
|
const gridStore = useLedgerStore((store) => store.gridStore);
|
||||||
|
const updateGridStore = useLedgerStore((store) => store.updateGridStore);
|
||||||
|
|
||||||
|
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
|
||||||
|
updateGridStore(colState);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!pubKey) {
|
||||||
|
return (
|
||||||
|
<Splash>
|
||||||
|
<p>{t('Please connect Vega wallet')}</p>
|
||||||
|
</Splash>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <LedgerManager partyId={pubKey} gridProps={gridStoreCallbacks} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useLedgerStore = create<DataGridSlice>()(
|
||||||
|
persist(createDataGridSlice, {
|
||||||
|
name: 'vega_ledger_store',
|
||||||
|
})
|
||||||
|
);
|
1
apps/trading/components/liquidity-container/index.ts
Normal file
1
apps/trading/components/liquidity-container/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './liquidity-container';
|
@ -0,0 +1,93 @@
|
|||||||
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
|
import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import {
|
||||||
|
lpAggregatedDataProvider,
|
||||||
|
type Filter,
|
||||||
|
LiquidityTable,
|
||||||
|
liquidityProvisionsDataProvider,
|
||||||
|
} from '@vegaprotocol/liquidity';
|
||||||
|
import { useMarket } from '@vegaprotocol/markets';
|
||||||
|
import {
|
||||||
|
NetworkParams,
|
||||||
|
useNetworkParams,
|
||||||
|
} from '@vegaprotocol/network-parameters';
|
||||||
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
|
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
|
||||||
|
export const LiquidityContainer = ({
|
||||||
|
marketId,
|
||||||
|
filter,
|
||||||
|
}: {
|
||||||
|
marketId: string | undefined;
|
||||||
|
filter?: Filter;
|
||||||
|
}) => {
|
||||||
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
|
|
||||||
|
const gridStore = useLiquidityStore((store) => store.gridStore);
|
||||||
|
const updateGridStore = useLiquidityStore((store) => store.updateGridStore);
|
||||||
|
|
||||||
|
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
|
||||||
|
updateGridStore(colState);
|
||||||
|
});
|
||||||
|
const { data: market } = useMarket(marketId);
|
||||||
|
|
||||||
|
// To be removed when liquidityProvision subscriptions are working
|
||||||
|
useReloadLiquidityData(marketId);
|
||||||
|
|
||||||
|
const { data, error } = useDataProvider({
|
||||||
|
dataProvider: lpAggregatedDataProvider,
|
||||||
|
variables: { marketId: marketId || '', filter },
|
||||||
|
skip: !marketId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const assetDecimalPlaces =
|
||||||
|
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
|
||||||
|
const quantum =
|
||||||
|
market?.tradableInstrument.instrument.product.settlementAsset.quantum || 0;
|
||||||
|
const symbol =
|
||||||
|
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||||
|
|
||||||
|
const { params } = useNetworkParams([
|
||||||
|
NetworkParams.market_liquidity_stakeToCcyVolume,
|
||||||
|
]);
|
||||||
|
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full relative">
|
||||||
|
<LiquidityTable
|
||||||
|
ref={gridRef}
|
||||||
|
rowData={data}
|
||||||
|
symbol={symbol}
|
||||||
|
assetDecimalPlaces={assetDecimalPlaces}
|
||||||
|
quantum={quantum}
|
||||||
|
stakeToCcyVolume={stakeToCcyVolume}
|
||||||
|
overlayNoRowsTemplate={error ? error.message : t('No data')}
|
||||||
|
{...gridStoreCallbacks}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const useReloadLiquidityData = (marketId: string | undefined) => {
|
||||||
|
const { reload } = useDataProvider({
|
||||||
|
dataProvider: liquidityProvisionsDataProvider,
|
||||||
|
variables: { marketId: marketId || '' },
|
||||||
|
update: () => true,
|
||||||
|
skip: !marketId,
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(reload, 30000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [reload]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const useLiquidityStore = create<DataGridSlice>()(
|
||||||
|
persist(createDataGridSlice, {
|
||||||
|
name: 'vega_ledger_store',
|
||||||
|
})
|
||||||
|
);
|
1
apps/trading/components/orders-container/index.ts
Normal file
1
apps/trading/components/orders-container/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './orders-container';
|
@ -0,0 +1,106 @@
|
|||||||
|
import { act, renderHook } from '@testing-library/react';
|
||||||
|
import {
|
||||||
|
FilterStatusValue,
|
||||||
|
STORAGE_KEY,
|
||||||
|
useOrderListGridState,
|
||||||
|
} from './orders-container';
|
||||||
|
import { Filter } from '@vegaprotocol/orders';
|
||||||
|
import { OrderType } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
describe('useOrderListGridState', () => {
|
||||||
|
afterAll(() => {
|
||||||
|
localStorage.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
const setup = (filter: Filter | undefined) => {
|
||||||
|
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) => {
|
||||||
|
const filterModel = {
|
||||||
|
type: {
|
||||||
|
value: [OrderType.TYPE_LIMIT],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { result } = setup(filter);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.updateGridState(filter, {
|
||||||
|
filterModel,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.gridState).toEqual({
|
||||||
|
columnState: undefined,
|
||||||
|
filterModel: {
|
||||||
|
...filterModel,
|
||||||
|
status: {
|
||||||
|
value: FilterStatusValue[filter],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const columnState = [{ colId: 'status', width: 200 }];
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.updateGridState(filter, {
|
||||||
|
columnState,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.gridState).toEqual({
|
||||||
|
columnState,
|
||||||
|
filterModel: {
|
||||||
|
...filterModel,
|
||||||
|
status: {
|
||||||
|
value: FilterStatusValue[filter],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const storeKeyMap = {
|
||||||
|
[Filter.Open]: 'open',
|
||||||
|
[Filter.Rejected]: 'rejected',
|
||||||
|
[Filter.Closed]: 'closed',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(JSON.parse(localStorage.getItem(STORAGE_KEY) || '')).toMatchObject(
|
||||||
|
{
|
||||||
|
state: {
|
||||||
|
[storeKeyMap[filter]]: {
|
||||||
|
columnState,
|
||||||
|
filterModel, // no need to check that status is set, hook will return status
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
166
apps/trading/components/orders-container/orders-container.tsx
Normal file
166
apps/trading/components/orders-container/orders-container.tsx
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { Filter } from '@vegaprotocol/orders';
|
||||||
|
import { OrderListManager } from '@vegaprotocol/orders';
|
||||||
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
|
import {
|
||||||
|
useMarketClickHandler,
|
||||||
|
useMarketLiquidityClickHandler,
|
||||||
|
} from '../../lib/hooks/use-market-click-handler';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
import type { DataGridStore } from '../../stores/datagrid-store-slice';
|
||||||
|
import { OrderStatus } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
export const FilterStatusValue = {
|
||||||
|
[Filter.Open]: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
||||||
|
[Filter.Closed]: [
|
||||||
|
OrderStatus.STATUS_CANCELLED,
|
||||||
|
OrderStatus.STATUS_EXPIRED,
|
||||||
|
OrderStatus.STATUS_FILLED,
|
||||||
|
OrderStatus.STATUS_PARTIALLY_FILLED,
|
||||||
|
OrderStatus.STATUS_STOPPED,
|
||||||
|
],
|
||||||
|
[Filter.Rejected]: [OrderStatus.STATUS_REJECTED],
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface OrderContainerProps {
|
||||||
|
marketId?: string;
|
||||||
|
filter?: Filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OrdersContainer = ({ marketId, filter }: OrderContainerProps) => {
|
||||||
|
const { pubKey, isReadOnly } = useVegaWallet();
|
||||||
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
|
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
||||||
|
const { gridState, updateGridState } = useOrderListGridState(filter);
|
||||||
|
const gridStoreCallbacks = useDataGridEvents(gridState, (newState) => {
|
||||||
|
updateGridState(filter, newState);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!pubKey) {
|
||||||
|
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<OrderListManager
|
||||||
|
partyId={pubKey}
|
||||||
|
marketId={marketId}
|
||||||
|
filter={filter}
|
||||||
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
|
gridProps={gridStoreCallbacks}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const STORAGE_KEY = 'vega_order_list_store';
|
||||||
|
const useOrderListStore = create<{
|
||||||
|
open: DataGridStore;
|
||||||
|
closed: DataGridStore;
|
||||||
|
rejected: DataGridStore;
|
||||||
|
all: DataGridStore;
|
||||||
|
update: (filter: Filter | undefined, gridStore: DataGridStore) => void;
|
||||||
|
}>()(
|
||||||
|
persist(
|
||||||
|
(set) => ({
|
||||||
|
open: {},
|
||||||
|
closed: {},
|
||||||
|
rejected: {},
|
||||||
|
all: {},
|
||||||
|
update: (filter, newStore) => {
|
||||||
|
switch (filter) {
|
||||||
|
case Filter.Open: {
|
||||||
|
set((curr) => ({
|
||||||
|
open: {
|
||||||
|
...curr.open,
|
||||||
|
...newStore,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Filter.Closed: {
|
||||||
|
set((curr) => ({
|
||||||
|
closed: {
|
||||||
|
...curr.closed,
|
||||||
|
...newStore,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Filter.Rejected: {
|
||||||
|
set((curr) => ({
|
||||||
|
rejected: {
|
||||||
|
...curr.rejected,
|
||||||
|
...newStore,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case undefined: {
|
||||||
|
set((curr) => ({
|
||||||
|
all: {
|
||||||
|
...curr.all,
|
||||||
|
...newStore,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: STORAGE_KEY,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const useOrderListGridState = (filter: Filter | undefined) => {
|
||||||
|
const updateGridState = useOrderListStore((store) => store.update);
|
||||||
|
const gridState = useOrderListStore((store) => {
|
||||||
|
// Return the column/filter state for the given filter but ensuring that
|
||||||
|
// each filter controlled by the tab is always applied
|
||||||
|
switch (filter) {
|
||||||
|
case Filter.Open: {
|
||||||
|
return {
|
||||||
|
columnState: store.open.columnState,
|
||||||
|
filterModel: {
|
||||||
|
...store.open.filterModel,
|
||||||
|
status: {
|
||||||
|
value: FilterStatusValue[Filter.Open],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case Filter.Closed: {
|
||||||
|
return {
|
||||||
|
columnState: store.closed.columnState,
|
||||||
|
filterModel: {
|
||||||
|
...store.closed.filterModel,
|
||||||
|
status: {
|
||||||
|
value: FilterStatusValue[Filter.Closed],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case Filter.Rejected: {
|
||||||
|
return {
|
||||||
|
columnState: store.rejected.columnState,
|
||||||
|
filterModel: {
|
||||||
|
...store.rejected.filterModel,
|
||||||
|
status: {
|
||||||
|
value: FilterStatusValue[Filter.Rejected],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return store.all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { gridState, updateGridState };
|
||||||
|
};
|
1
apps/trading/components/positions-container/index.ts
Normal file
1
apps/trading/components/positions-container/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './positions-container';
|
@ -1,21 +1,28 @@
|
|||||||
|
import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { PositionsManager } from '@vegaprotocol/positions';
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { PositionsManager } from './positions-manager';
|
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
|
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
|
||||||
export const PositionsContainer = ({
|
export const PositionsContainer = ({
|
||||||
onMarketClick,
|
onMarketClick,
|
||||||
noBottomPlaceholder,
|
|
||||||
storeKey,
|
|
||||||
allKeys,
|
allKeys,
|
||||||
}: {
|
}: {
|
||||||
onMarketClick?: (marketId: string) => void;
|
onMarketClick?: (marketId: string) => void;
|
||||||
noBottomPlaceholder?: boolean;
|
|
||||||
storeKey?: string;
|
|
||||||
allKeys?: boolean;
|
allKeys?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
|
const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
|
||||||
|
|
||||||
|
const gridStore = usePositionsStore((store) => store.gridStore);
|
||||||
|
const updateGridStore = usePositionsStore((store) => store.updateGridStore);
|
||||||
|
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
|
||||||
|
updateGridStore(colState);
|
||||||
|
});
|
||||||
|
|
||||||
if (!pubKey) {
|
if (!pubKey) {
|
||||||
return (
|
return (
|
||||||
<Splash>
|
<Splash>
|
||||||
@ -38,8 +45,13 @@ export const PositionsContainer = ({
|
|||||||
partyIds={partyIds}
|
partyIds={partyIds}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
noBottomPlaceholder={noBottomPlaceholder}
|
gridProps={gridStoreCallbacks}
|
||||||
storeKey={storeKey}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const usePositionsStore = create<DataGridSlice>()(
|
||||||
|
persist(createDataGridSlice, {
|
||||||
|
name: 'vega_positions_store',
|
||||||
|
})
|
||||||
|
);
|
25
apps/trading/stores/datagrid-store-slice.ts
Normal file
25
apps/trading/stores/datagrid-store-slice.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import type { ColumnState } from 'ag-grid-community';
|
||||||
|
import type { StateCreator } from 'zustand';
|
||||||
|
|
||||||
|
export type DataGridStore = {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
filterModel?: { [key: string]: any };
|
||||||
|
columnState?: ColumnState[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DataGridSlice = {
|
||||||
|
gridStore: DataGridStore;
|
||||||
|
updateGridStore: (gridStore: DataGridStore) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createDataGridSlice: StateCreator<DataGridSlice> = (set) => ({
|
||||||
|
gridStore: {},
|
||||||
|
updateGridStore: (newStore) => {
|
||||||
|
set((curr) => ({
|
||||||
|
gridStore: {
|
||||||
|
...curr.gridStore,
|
||||||
|
...newStore,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
});
|
@ -7,6 +7,7 @@ import {
|
|||||||
} from '@testing-library/react';
|
} from '@testing-library/react';
|
||||||
import * as helpers from '@vegaprotocol/data-provider';
|
import * as helpers from '@vegaprotocol/data-provider';
|
||||||
import { AccountManager } from './accounts-manager';
|
import { AccountManager } from './accounts-manager';
|
||||||
|
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
|
|
||||||
const mockedUseDataProvider = jest.fn();
|
const mockedUseDataProvider = jest.fn();
|
||||||
jest.mock('@vegaprotocol/data-provider', () => ({
|
jest.mock('@vegaprotocol/data-provider', () => ({
|
||||||
@ -14,6 +15,13 @@ jest.mock('@vegaprotocol/data-provider', () => ({
|
|||||||
useDataProvider: jest.fn(() => mockedUseDataProvider()),
|
useDataProvider: jest.fn(() => mockedUseDataProvider()),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const gridProps = {
|
||||||
|
onGridReady: jest.fn(),
|
||||||
|
onColumnResized: jest.fn(),
|
||||||
|
onFilterChanged: jest.fn(),
|
||||||
|
onSortChanged: jest.fn(),
|
||||||
|
} as unknown as ReturnType<typeof useDataGridEvents>;
|
||||||
|
|
||||||
describe('AccountManager', () => {
|
describe('AccountManager', () => {
|
||||||
describe('when rerender', () => {
|
describe('when rerender', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -43,6 +51,7 @@ describe('AccountManager', () => {
|
|||||||
partyId="partyOne"
|
partyId="partyOne"
|
||||||
onClickAsset={jest.fn}
|
onClickAsset={jest.fn}
|
||||||
isReadOnly={false}
|
isReadOnly={false}
|
||||||
|
gridProps={gridProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
@ -55,6 +64,7 @@ describe('AccountManager', () => {
|
|||||||
partyId="partyTwo"
|
partyId="partyTwo"
|
||||||
onClickAsset={jest.fn}
|
onClickAsset={jest.fn}
|
||||||
isReadOnly={false}
|
isReadOnly={false}
|
||||||
|
gridProps={gridProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -72,6 +82,7 @@ describe('AccountManager', () => {
|
|||||||
partyId="partyOne"
|
partyId="partyOne"
|
||||||
onClickAsset={jest.fn}
|
onClickAsset={jest.fn}
|
||||||
isReadOnly={false}
|
isReadOnly={false}
|
||||||
|
gridProps={gridProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
rerenderer = rerender;
|
rerenderer = rerender;
|
||||||
@ -85,6 +96,7 @@ describe('AccountManager', () => {
|
|||||||
partyId="partyOne"
|
partyId="partyOne"
|
||||||
onClickAsset={jest.fn}
|
onClickAsset={jest.fn}
|
||||||
isReadOnly={false}
|
isReadOnly={false}
|
||||||
|
gridProps={gridProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -110,6 +122,7 @@ describe('AccountManager', () => {
|
|||||||
partyId="partyOne"
|
partyId="partyOne"
|
||||||
onClickAsset={jest.fn}
|
onClickAsset={jest.fn}
|
||||||
isReadOnly={false}
|
isReadOnly={false}
|
||||||
|
gridProps={gridProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -11,6 +11,7 @@ import type { PinnedAsset } from './accounts-table';
|
|||||||
import { AccountTable } from './accounts-table';
|
import { AccountTable } from './accounts-table';
|
||||||
import { Dialog } from '@vegaprotocol/ui-toolkit';
|
import { Dialog } from '@vegaprotocol/ui-toolkit';
|
||||||
import BreakdownTable from './breakdown-table';
|
import BreakdownTable from './breakdown-table';
|
||||||
|
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
|
|
||||||
const AccountBreakdown = ({
|
const AccountBreakdown = ({
|
||||||
assetId,
|
assetId,
|
||||||
@ -102,7 +103,7 @@ interface AccountManagerProps {
|
|||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
pinnedAsset?: PinnedAsset;
|
pinnedAsset?: PinnedAsset;
|
||||||
storeKey?: string;
|
gridProps?: ReturnType<typeof useDataGridEvents>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AccountManager = ({
|
export const AccountManager = ({
|
||||||
@ -112,12 +113,11 @@ export const AccountManager = ({
|
|||||||
partyId,
|
partyId,
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
pinnedAsset,
|
pinnedAsset,
|
||||||
storeKey,
|
|
||||||
onMarketClick,
|
onMarketClick,
|
||||||
|
gridProps,
|
||||||
}: AccountManagerProps) => {
|
}: AccountManagerProps) => {
|
||||||
const gridRef = useRef<AgGridReact | null>(null);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
const [breakdownAssetId, setBreakdownAssetId] = useState<string>();
|
const [breakdownAssetId, setBreakdownAssetId] = useState<string>();
|
||||||
|
|
||||||
const { data, error } = useDataProvider({
|
const { data, error } = useDataProvider({
|
||||||
dataProvider: aggregatedAccountsDataProvider,
|
dataProvider: aggregatedAccountsDataProvider,
|
||||||
variables: { partyId },
|
variables: { partyId },
|
||||||
@ -144,8 +144,8 @@ export const AccountManager = ({
|
|||||||
onClickBreakdown={setBreakdownAssetId}
|
onClickBreakdown={setBreakdownAssetId}
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
pinnedAsset={pinnedAsset}
|
pinnedAsset={pinnedAsset}
|
||||||
storeKey={storeKey}
|
|
||||||
overlayNoRowsTemplate={error ? error.message : t('No accounts')}
|
overlayNoRowsTemplate={error ? error.message : t('No accounts')}
|
||||||
|
{...gridProps}
|
||||||
/>
|
/>
|
||||||
<AccountBreakdownDialog
|
<AccountBreakdownDialog
|
||||||
assetId={breakdownAssetId}
|
assetId={breakdownAssetId}
|
||||||
|
@ -73,7 +73,6 @@ export interface AccountTableProps extends AgGridReactProps {
|
|||||||
onClickBreakdown?: (assetId: string) => void;
|
onClickBreakdown?: (assetId: string) => void;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
pinnedAsset?: PinnedAsset;
|
pinnedAsset?: PinnedAsset;
|
||||||
storeKey?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
||||||
@ -307,7 +306,6 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
|||||||
<AgGrid
|
<AgGrid
|
||||||
{...props}
|
{...props}
|
||||||
style={{ width: '100%', height: '100%' }}
|
style={{ width: '100%', height: '100%' }}
|
||||||
overlayNoRowsTemplate={t('No accounts')}
|
|
||||||
getRowId={({ data }: { data: AccountFields }) => data.asset.id}
|
getRowId={({ data }: { data: AccountFields }) => data.asset.id}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
tooltipShowDelay={500}
|
tooltipShowDelay={500}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
export * from './lib/ag-grid/ag-grid-lazy';
|
export * from './lib/ag-grid/ag-grid-lazy';
|
||||||
export * from './lib/ag-grid/use-column-sizes';
|
|
||||||
|
|
||||||
export * from './lib/column-definitions';
|
export * from './lib/column-definitions';
|
||||||
|
|
||||||
@ -24,4 +23,4 @@ export * from './lib/type-helpers';
|
|||||||
export * from './lib/cells/grid-progress-bar';
|
export * from './lib/cells/grid-progress-bar';
|
||||||
|
|
||||||
export * from './lib/ag-grid-update';
|
export * from './lib/ag-grid-update';
|
||||||
export * from './lib/use-bottom-placeholder';
|
export * from './lib/use-datagrid-events';
|
||||||
|
@ -2,23 +2,16 @@ import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
|
|||||||
import { AgGridReact } from 'ag-grid-react';
|
import { AgGridReact } from 'ag-grid-react';
|
||||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useColumnSizes } from './use-column-sizes';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
export const AgGridThemed = ({
|
export const AgGridThemed = ({
|
||||||
style,
|
style,
|
||||||
gridRef,
|
gridRef,
|
||||||
storeKey,
|
|
||||||
...props
|
...props
|
||||||
}: (AgGridReactProps | AgReactUiProps) & {
|
}: (AgGridReactProps | AgReactUiProps) & {
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
gridRef?: React.ForwardedRef<AgGridReact>;
|
gridRef?: React.ForwardedRef<AgGridReact>;
|
||||||
storeKey?: string;
|
|
||||||
}) => {
|
}) => {
|
||||||
const commonColumnCallbacks = useColumnSizes({
|
|
||||||
storeKey,
|
|
||||||
props,
|
|
||||||
});
|
|
||||||
const { theme } = useThemeSwitcher();
|
const { theme } = useThemeSwitcher();
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
rowHeight: 22,
|
rowHeight: 22,
|
||||||
@ -36,12 +29,7 @@ export const AgGridThemed = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={wrapperClasses} style={style}>
|
<div className={wrapperClasses} style={style}>
|
||||||
<AgGridReact
|
<AgGridReact {...defaultProps} {...props} ref={gridRef} />
|
||||||
{...defaultProps}
|
|
||||||
{...props}
|
|
||||||
{...commonColumnCallbacks}
|
|
||||||
ref={gridRef}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,6 @@ import type { AgGridReactProps, AgGridReact } from 'ag-grid-react';
|
|||||||
type Props = AgGridReactProps & {
|
type Props = AgGridReactProps & {
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
gridRef?: React.Ref<AgGridReact>;
|
gridRef?: React.Ref<AgGridReact>;
|
||||||
storeKey?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AgGridLazyInternal = lazy(() =>
|
export const AgGridLazyInternal = lazy(() =>
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
import type {
|
|
||||||
Column,
|
|
||||||
ColumnResizedEvent,
|
|
||||||
GridSizeChangedEvent,
|
|
||||||
GridReadyEvent,
|
|
||||||
} from 'ag-grid-community';
|
|
||||||
import { renderHook, act, waitFor } from '@testing-library/react';
|
|
||||||
import { useColumnSizes } from './use-column-sizes';
|
|
||||||
import * as reactHelpers from '@vegaprotocol/react-helpers';
|
|
||||||
|
|
||||||
const mockApis = {
|
|
||||||
api: {
|
|
||||||
sizeColumnsToFit: jest.fn(),
|
|
||||||
},
|
|
||||||
columnApi: {
|
|
||||||
setColumnWidths: jest.fn(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockValueSetter = jest.fn();
|
|
||||||
const mockStore = {
|
|
||||||
sizes: { testid: { col1: 100 } },
|
|
||||||
valueSetter: mockValueSetter,
|
|
||||||
};
|
|
||||||
jest.mock('zustand', () => ({
|
|
||||||
...jest.requireActual('zustand'),
|
|
||||||
create: () =>
|
|
||||||
jest.fn(() =>
|
|
||||||
jest.fn().mockImplementation((creator) => {
|
|
||||||
return creator(mockStore);
|
|
||||||
})
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
describe('UseColumnSizes hook', () => {
|
|
||||||
const storeKey = 'testid';
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
|
||||||
it('should return proper methods', () => {
|
|
||||||
const { result } = renderHook(() =>
|
|
||||||
useColumnSizes({ storeKey, props: {} })
|
|
||||||
);
|
|
||||||
expect(Object.keys(result.current)).toHaveLength(3);
|
|
||||||
expect(result.current).toStrictEqual({
|
|
||||||
onColumnResized: expect.any(Function),
|
|
||||||
onGridReady: expect.any(Function),
|
|
||||||
onGridSizeChanged: expect.any(Function),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('onGridSizeChanged should call setSize', async () => {
|
|
||||||
jest
|
|
||||||
.spyOn(reactHelpers, 'useScreenDimensions')
|
|
||||||
.mockReturnValue({ screenSize: 'xxl' });
|
|
||||||
const { result } = renderHook(() =>
|
|
||||||
useColumnSizes({ storeKey, props: {} })
|
|
||||||
);
|
|
||||||
await act(() => {
|
|
||||||
result.current.onGridSizeChanged?.({
|
|
||||||
clientWidth: 1000,
|
|
||||||
...mockApis,
|
|
||||||
} as GridSizeChangedEvent);
|
|
||||||
});
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(mockApis.columnApi.setColumnWidths).toHaveBeenCalledWith([
|
|
||||||
{ key: 'col1', newWidth: 100 },
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('onColumnResized should fill up store', async () => {
|
|
||||||
const columns: Column[] = [
|
|
||||||
{ getColId: () => 'col1', getActualWidth: () => 100 },
|
|
||||||
{ getColId: () => 'col2', getActualWidth: () => 200 },
|
|
||||||
] as Column[];
|
|
||||||
const sizeObj = { col1: 100, col2: 200, clientWidth: 1000 };
|
|
||||||
const { result } = renderHook(() =>
|
|
||||||
useColumnSizes({ storeKey, props: {} })
|
|
||||||
);
|
|
||||||
await act(() => {
|
|
||||||
result.current.onGridSizeChanged?.({
|
|
||||||
clientWidth: 1000,
|
|
||||||
...mockApis,
|
|
||||||
} as GridSizeChangedEvent);
|
|
||||||
});
|
|
||||||
await act(() => {
|
|
||||||
result.current.onColumnResized?.({
|
|
||||||
columns,
|
|
||||||
finished: true,
|
|
||||||
source: 'uiColumnDragged',
|
|
||||||
...mockApis,
|
|
||||||
} as ColumnResizedEvent);
|
|
||||||
});
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(mockValueSetter).toHaveBeenCalledWith(storeKey, sizeObj);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('onGridReady should call setSizes', async () => {
|
|
||||||
const props = { onGridReady: jest.fn() };
|
|
||||||
|
|
||||||
const { result } = renderHook(() => useColumnSizes({ storeKey, props }));
|
|
||||||
const obTest = { cool: 1, ...mockApis };
|
|
||||||
await act(() => {
|
|
||||||
result.current.onGridReady?.(obTest as GridReadyEvent);
|
|
||||||
});
|
|
||||||
expect(props.onGridReady).toHaveBeenCalledWith(obTest);
|
|
||||||
expect(mockApis.api.sizeColumnsToFit).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('if no storeKey should be transparent', () => {
|
|
||||||
const { result } = renderHook(() =>
|
|
||||||
useColumnSizes({ storeKey: '', props: {} })
|
|
||||||
);
|
|
||||||
expect(result.current).toStrictEqual({
|
|
||||||
onColumnResized: undefined,
|
|
||||||
onGridReady: undefined,
|
|
||||||
onGridSizeChanged: undefined,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,145 +0,0 @@
|
|||||||
import { useCallback, useRef } from 'react';
|
|
||||||
import type {
|
|
||||||
GridSizeChangedEvent,
|
|
||||||
GridReadyEvent,
|
|
||||||
ColumnResizedEvent,
|
|
||||||
} from 'ag-grid-community';
|
|
||||||
import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
|
|
||||||
import { create } from 'zustand';
|
|
||||||
import { persist } from 'zustand/middleware';
|
|
||||||
import { immer } from 'zustand/middleware/immer';
|
|
||||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
|
||||||
|
|
||||||
const STORAGE_KEY = 'vega_columns_sizes_store';
|
|
||||||
|
|
||||||
export const useColumnSizesStore = create<{
|
|
||||||
sizes: Record<string, Record<string, number>>;
|
|
||||||
valueSetter: (storeKey: string, value: Record<string, number>) => void;
|
|
||||||
}>()(
|
|
||||||
persist(
|
|
||||||
immer((set) => ({
|
|
||||||
sizes: {},
|
|
||||||
valueSetter: (storeKey, value) =>
|
|
||||||
set((state) => {
|
|
||||||
state.sizes[storeKey] = {
|
|
||||||
...(state.sizes[storeKey] || {}),
|
|
||||||
...value,
|
|
||||||
};
|
|
||||||
return state;
|
|
||||||
}),
|
|
||||||
})),
|
|
||||||
{ name: STORAGE_KEY }
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
interface UseColumnSizesProps {
|
|
||||||
props: AgGridReactProps | AgReactUiProps;
|
|
||||||
storeKey?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useColumnSizes = ({
|
|
||||||
storeKey = '',
|
|
||||||
props,
|
|
||||||
}: UseColumnSizesProps): {
|
|
||||||
onColumnResized?: (event: ColumnResizedEvent) => void;
|
|
||||||
onGridReady?: (event: GridReadyEvent) => void;
|
|
||||||
onGridSizeChanged?: (event: GridSizeChangedEvent) => void;
|
|
||||||
} => {
|
|
||||||
const sizes = useColumnSizesStore((store) => store.sizes[storeKey] || {});
|
|
||||||
const valueSetter = useColumnSizesStore((store) => store.valueSetter);
|
|
||||||
const widthRef = useRef(sizes['clientWidth'] || 0);
|
|
||||||
const {
|
|
||||||
onColumnResized: parentOnColumnResized,
|
|
||||||
onGridReady: parentOnGridReady,
|
|
||||||
onGridSizeChanged: parentOnGridSizeChanged,
|
|
||||||
} = props;
|
|
||||||
const recalculateSizes = useCallback((sizes: Record<string, number>) => {
|
|
||||||
if (
|
|
||||||
widthRef.current &&
|
|
||||||
sizes['clientWidth'] &&
|
|
||||||
widthRef.current !== sizes['clientWidth']
|
|
||||||
) {
|
|
||||||
const oldWidth = sizes['clientWidth'];
|
|
||||||
const ratio = widthRef.current / oldWidth;
|
|
||||||
return {
|
|
||||||
...Object.entries(sizes).reduce((agg, [key, value]) => {
|
|
||||||
agg[key] = value * ratio;
|
|
||||||
return agg;
|
|
||||||
}, {} as Record<string, number>),
|
|
||||||
width: widthRef.current,
|
|
||||||
} as Record<string, number>;
|
|
||||||
}
|
|
||||||
return sizes;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onColumnResized = useCallback(
|
|
||||||
(event: ColumnResizedEvent) => {
|
|
||||||
parentOnColumnResized?.(event);
|
|
||||||
if (
|
|
||||||
storeKey &&
|
|
||||||
event.source === 'uiColumnDragged' &&
|
|
||||||
event.finished &&
|
|
||||||
widthRef.current
|
|
||||||
) {
|
|
||||||
const { columns } = event;
|
|
||||||
if (columns?.length) {
|
|
||||||
const sizesObj = columns.reduce((aggr, column) => {
|
|
||||||
aggr[column.getColId()] = column.getActualWidth();
|
|
||||||
return aggr;
|
|
||||||
}, {} as Record<string, number>);
|
|
||||||
sizesObj['clientWidth'] = widthRef.current;
|
|
||||||
valueSetter(storeKey, sizesObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[valueSetter, storeKey, parentOnColumnResized]
|
|
||||||
);
|
|
||||||
const { screenSize } = useScreenDimensions();
|
|
||||||
const largeScreen = ['xl', 'xxl', 'xxxl'].includes(screenSize);
|
|
||||||
const setSizes = useCallback(
|
|
||||||
(apiEvent: GridReadyEvent | GridSizeChangedEvent) => {
|
|
||||||
if (!storeKey || !Object.keys(sizes).length || !widthRef.current) {
|
|
||||||
largeScreen && apiEvent?.api.sizeColumnsToFit();
|
|
||||||
} else {
|
|
||||||
const recalculatedSizes = recalculateSizes(sizes);
|
|
||||||
const newSizes = Object.entries(recalculatedSizes).map(
|
|
||||||
([key, size]) => ({
|
|
||||||
key,
|
|
||||||
newWidth: size,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
apiEvent.columnApi.setColumnWidths(newSizes);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[storeKey, recalculateSizes, sizes, largeScreen]
|
|
||||||
);
|
|
||||||
|
|
||||||
const onGridReady = useCallback(
|
|
||||||
(event: GridReadyEvent) => {
|
|
||||||
parentOnGridReady?.(event);
|
|
||||||
setSizes(event);
|
|
||||||
},
|
|
||||||
[setSizes, parentOnGridReady]
|
|
||||||
);
|
|
||||||
|
|
||||||
const onGridSizeChanged = useCallback(
|
|
||||||
(event: GridSizeChangedEvent) => {
|
|
||||||
parentOnGridSizeChanged?.(event);
|
|
||||||
widthRef.current = event.clientWidth;
|
|
||||||
setSizes(event);
|
|
||||||
},
|
|
||||||
[parentOnGridSizeChanged, setSizes]
|
|
||||||
);
|
|
||||||
if (storeKey) {
|
|
||||||
return {
|
|
||||||
onGridReady,
|
|
||||||
onGridSizeChanged,
|
|
||||||
onColumnResized,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
onGridReady: parentOnGridReady,
|
|
||||||
onGridSizeChanged: parentOnGridSizeChanged,
|
|
||||||
onColumnResized: parentOnColumnResized,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,68 +0,0 @@
|
|||||||
import type { RefObject } from 'react';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
import type { AgGridReact } from 'ag-grid-react';
|
|
||||||
import type { IsFullWidthRowParams, RowHeightParams } from 'ag-grid-community';
|
|
||||||
|
|
||||||
const NO_HOVER_CSS_RULE = { 'no-hover': 'data?.isLastPlaceholder' };
|
|
||||||
const ROW_ID = 'bottom-placeholder';
|
|
||||||
const fullWidthCellRenderer = () => null;
|
|
||||||
const isFullWidthRow = (params: IsFullWidthRowParams) =>
|
|
||||||
params.rowNode.data?.isLastPlaceholder;
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
gridRef: RefObject<AgGridReact>;
|
|
||||||
disabled?: boolean;
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
||||||
export const useBottomPlaceholder = ({ gridRef, disabled }: Props) => {
|
|
||||||
const onBodyScrollEnd = useCallback(() => {
|
|
||||||
const rowCont = gridRef.current?.api.getDisplayedRowCount() ?? 0;
|
|
||||||
if (rowCont) {
|
|
||||||
const lastRow = gridRef.current?.api.getDisplayedRowAtIndex(rowCont - 1);
|
|
||||||
if (lastRow && lastRow.data) {
|
|
||||||
const placeholderRow = {
|
|
||||||
...lastRow.data,
|
|
||||||
isLastPlaceholder: true,
|
|
||||||
id: ROW_ID,
|
|
||||||
};
|
|
||||||
const transaction = gridRef.current?.api.getRowNode(ROW_ID)
|
|
||||||
? { update: [placeholderRow] }
|
|
||||||
: { add: [placeholderRow] };
|
|
||||||
gridRef.current?.api.applyTransaction(transaction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [gridRef]);
|
|
||||||
|
|
||||||
const onRowsChanged = useCallback(() => {
|
|
||||||
const placeholderNode = gridRef.current?.api.getRowNode(ROW_ID);
|
|
||||||
if (placeholderNode) {
|
|
||||||
const transaction = {
|
|
||||||
remove: [placeholderNode.data],
|
|
||||||
};
|
|
||||||
gridRef.current?.api.applyTransaction(transaction);
|
|
||||||
}
|
|
||||||
onBodyScrollEnd();
|
|
||||||
}, [gridRef, onBodyScrollEnd]);
|
|
||||||
|
|
||||||
const getRowHeight = useCallback(
|
|
||||||
(params: RowHeightParams) =>
|
|
||||||
params.data?.isLastPlaceholder ? 50 : undefined,
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
return useMemo(
|
|
||||||
() =>
|
|
||||||
!disabled
|
|
||||||
? {
|
|
||||||
onBodyScrollEnd,
|
|
||||||
rowClassRules: NO_HOVER_CSS_RULE,
|
|
||||||
isFullWidthRow,
|
|
||||||
fullWidthCellRenderer,
|
|
||||||
onSortChanged: onRowsChanged,
|
|
||||||
onFilterChanged: onRowsChanged,
|
|
||||||
getRowHeight,
|
|
||||||
}
|
|
||||||
: {},
|
|
||||||
[onBodyScrollEnd, onRowsChanged, disabled, getRowHeight]
|
|
||||||
);
|
|
||||||
};
|
|
185
libs/datagrid/src/lib/use-datagrid-events.spec.tsx
Normal file
185
libs/datagrid/src/lib/use-datagrid-events.spec.tsx
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
import { act, render, waitFor } from '@testing-library/react';
|
||||||
|
import {
|
||||||
|
useDataGridEvents,
|
||||||
|
GRID_EVENT_DEBOUNCE_TIME,
|
||||||
|
} from './use-datagrid-events';
|
||||||
|
import { AgGridThemed } from './ag-grid/ag-grid-lazy-themed';
|
||||||
|
import type { MutableRefObject } from 'react';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
|
||||||
|
const gridProps = {
|
||||||
|
rowData: [{ id: 1 }],
|
||||||
|
columnDefs: [
|
||||||
|
{
|
||||||
|
field: 'id',
|
||||||
|
width: 100,
|
||||||
|
resizable: true,
|
||||||
|
filter: 'agNumberColumnFilter',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
style: { width: 500, height: 300 },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Not using render hook so I can pass event callbacks
|
||||||
|
// to a rendered grid
|
||||||
|
function setup(...args: Parameters<typeof useDataGridEvents>) {
|
||||||
|
let gridRef;
|
||||||
|
|
||||||
|
function TestComponent() {
|
||||||
|
const hookCallbacks = useDataGridEvents(...args);
|
||||||
|
gridRef = useRef<AgGridReact | null>(null);
|
||||||
|
return <AgGridThemed gridRef={gridRef} {...gridProps} {...hookCallbacks} />;
|
||||||
|
}
|
||||||
|
render(<TestComponent />);
|
||||||
|
return gridRef as unknown as MutableRefObject<AgGridReact>;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('useDataGridEvents', () => {
|
||||||
|
const originalWarn = console.warn;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
// disabling some ag grid warnings that are caused by test setup only
|
||||||
|
console.warn = () => undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
jest.useRealTimers();
|
||||||
|
console.warn = originalWarn;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('default state is set and callback is called on column or filter event', async () => {
|
||||||
|
const callback = jest.fn();
|
||||||
|
const initialState = {
|
||||||
|
filterModel: undefined,
|
||||||
|
columnState: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = setup(initialState, callback);
|
||||||
|
|
||||||
|
// column state was not updated, so the default width provided by the
|
||||||
|
// col def should be set
|
||||||
|
expect(result.current.columnApi.getColumnState()[0].width).toEqual(
|
||||||
|
gridProps.columnDefs[0].width
|
||||||
|
);
|
||||||
|
// no filters set
|
||||||
|
expect(result.current.api.getFilterModel()).toEqual({});
|
||||||
|
|
||||||
|
const newWidth = 400;
|
||||||
|
|
||||||
|
// Set col width
|
||||||
|
await act(async () => {
|
||||||
|
result.current.columnApi.setColumnWidth('id', newWidth);
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(callback).toHaveBeenCalledWith({
|
||||||
|
columnState: [expect.objectContaining({ colId: 'id', width: newWidth })],
|
||||||
|
filterModel: {},
|
||||||
|
});
|
||||||
|
callback.mockClear();
|
||||||
|
expect(result.current.columnApi.getColumnState()[0].width).toEqual(
|
||||||
|
newWidth
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set filter
|
||||||
|
await act(async () => {
|
||||||
|
result.current.columnApi.applyColumnState({
|
||||||
|
state: [{ colId: 'id', sort: 'asc' }],
|
||||||
|
applyOrder: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(callback).toHaveBeenCalledWith({
|
||||||
|
columnState: [expect.objectContaining({ colId: 'id', sort: 'asc' })],
|
||||||
|
filterModel: {},
|
||||||
|
});
|
||||||
|
callback.mockClear();
|
||||||
|
expect(result.current.columnApi.getColumnState()[0].sort).toEqual('asc');
|
||||||
|
|
||||||
|
// Set filter
|
||||||
|
const idFilter = {
|
||||||
|
filter: 1,
|
||||||
|
filterType: 'number',
|
||||||
|
type: 'equals',
|
||||||
|
};
|
||||||
|
await act(async () => {
|
||||||
|
result.current.api.setFilterModel({
|
||||||
|
id: idFilter,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(callback).toHaveBeenCalledWith({
|
||||||
|
columnState: expect.any(Object),
|
||||||
|
filterModel: {
|
||||||
|
id: idFilter,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
callback.mockClear();
|
||||||
|
expect(result.current.api.getFilterModel()['id']).toEqual(idFilter);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies grid state on ready', async () => {
|
||||||
|
const idFilter = {
|
||||||
|
filter: 1,
|
||||||
|
filterType: 'number',
|
||||||
|
type: 'equals',
|
||||||
|
};
|
||||||
|
const colState = { colId: 'id', width: 300, sort: 'desc' as const };
|
||||||
|
const initialState = {
|
||||||
|
filterModel: {
|
||||||
|
id: idFilter,
|
||||||
|
},
|
||||||
|
columnState: [colState],
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = setup(initialState, jest.fn());
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.api.getFilterModel()['id']).toEqual(idFilter);
|
||||||
|
expect(result.current.columnApi.getColumnState()[0]).toEqual(
|
||||||
|
expect.objectContaining(colState)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('debounces events', async () => {
|
||||||
|
const callback = jest.fn();
|
||||||
|
const initialState = {
|
||||||
|
filterModel: undefined,
|
||||||
|
columnState: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = setup(initialState, callback);
|
||||||
|
|
||||||
|
const newWidth = 400;
|
||||||
|
|
||||||
|
// Set col width multiple times
|
||||||
|
await act(async () => {
|
||||||
|
result.current.columnApi.setColumnWidth('id', newWidth);
|
||||||
|
result.current.columnApi.setColumnWidth('id', newWidth);
|
||||||
|
result.current.columnApi.setColumnWidth('id', newWidth);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(callback).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
66
libs/datagrid/src/lib/use-datagrid-events.ts
Normal file
66
libs/datagrid/src/lib/use-datagrid-events.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import debounce from 'lodash/debounce';
|
||||||
|
import type {
|
||||||
|
ColumnResizedEvent,
|
||||||
|
ColumnState,
|
||||||
|
FilterChangedEvent,
|
||||||
|
GridReadyEvent,
|
||||||
|
SortChangedEvent,
|
||||||
|
} from 'ag-grid-community';
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
filterModel?: { [key: string]: any };
|
||||||
|
columnState?: ColumnState[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type Event = ColumnResizedEvent | FilterChangedEvent | SortChangedEvent;
|
||||||
|
|
||||||
|
export const GRID_EVENT_DEBOUNCE_TIME = 300;
|
||||||
|
|
||||||
|
export const useDataGridEvents = (
|
||||||
|
state: State,
|
||||||
|
callback: (data: State) => void
|
||||||
|
) => {
|
||||||
|
// This function can be called very frequently by the onColumnResized
|
||||||
|
// grid callback, so its memoized to only update after resizing is finished
|
||||||
|
const onGridChange = useMemo(
|
||||||
|
() =>
|
||||||
|
debounce(({ api, columnApi }: Event) => {
|
||||||
|
if (!api || !columnApi) return;
|
||||||
|
const columnState = columnApi.getColumnState();
|
||||||
|
const filterModel = api.getFilterModel();
|
||||||
|
callback({ columnState, filterModel });
|
||||||
|
}, GRID_EVENT_DEBOUNCE_TIME),
|
||||||
|
[callback]
|
||||||
|
);
|
||||||
|
|
||||||
|
// check if we have stored column states or filter models and apply if we do
|
||||||
|
const onGridReady = useCallback(
|
||||||
|
({ api, columnApi }: GridReadyEvent) => {
|
||||||
|
if (!api || !columnApi) return;
|
||||||
|
|
||||||
|
if (state.columnState) {
|
||||||
|
columnApi.applyColumnState({
|
||||||
|
state: state.columnState,
|
||||||
|
applyOrder: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// ensure columns fit available space if no widths are set
|
||||||
|
api.sizeColumnsToFit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.filterModel) {
|
||||||
|
api.setFilterModel(state.filterModel);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[state]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
onGridReady,
|
||||||
|
onColumnResized: onGridChange,
|
||||||
|
onFilterChanged: onGridChange,
|
||||||
|
onSortChanged: onGridChange,
|
||||||
|
};
|
||||||
|
};
|
@ -24,10 +24,8 @@ export const DepositsTable = forwardRef<
|
|||||||
return (
|
return (
|
||||||
<AgGrid
|
<AgGrid
|
||||||
ref={ref}
|
ref={ref}
|
||||||
defaultColDef={{ resizable: true }}
|
defaultColDef={{ flex: 1 }}
|
||||||
style={{ width: '100%', height: '100%' }}
|
style={{ width: '100%', height: '100%' }}
|
||||||
suppressCellFocus={true}
|
|
||||||
storeKey="depositTable"
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<AgGridColumn headerName="Asset" field="asset.symbol" />
|
<AgGridColumn headerName="Asset" field="asset.symbol" />
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export * from './lib/fills-container';
|
export * from './lib/fills-manager';
|
||||||
export * from './lib/fills-data-provider';
|
export * from './lib/fills-data-provider';
|
||||||
export * from './lib/__generated__/Fills';
|
export * from './lib/__generated__/Fills';
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
|
||||||
import { FillsManager } from './fills-manager';
|
|
||||||
|
|
||||||
export const FillsContainer = ({
|
|
||||||
marketId,
|
|
||||||
onMarketClick,
|
|
||||||
storeKey,
|
|
||||||
}: {
|
|
||||||
marketId?: string;
|
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
|
||||||
storeKey?: string;
|
|
||||||
}) => {
|
|
||||||
const { pubKey } = useVegaWallet();
|
|
||||||
|
|
||||||
if (!pubKey) {
|
|
||||||
return (
|
|
||||||
<Splash>
|
|
||||||
<p>{t('Please connect Vega wallet')}</p>
|
|
||||||
</Splash>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FillsManager
|
|
||||||
partyId={pubKey}
|
|
||||||
marketId={marketId}
|
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
storeKey={storeKey}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -2,7 +2,7 @@ import type { AgGridReact } from 'ag-grid-react';
|
|||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { FillsTable } from './fills-table';
|
import { FillsTable } from './fills-table';
|
||||||
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
|
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
import type * as Schema from '@vegaprotocol/types';
|
import type * as Schema from '@vegaprotocol/types';
|
||||||
import { fillsWithMarketProvider } from './fills-data-provider';
|
import { fillsWithMarketProvider } from './fills-data-provider';
|
||||||
@ -11,14 +11,14 @@ interface FillsManagerProps {
|
|||||||
partyId: string;
|
partyId: string;
|
||||||
marketId?: string;
|
marketId?: string;
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
storeKey?: string;
|
gridProps: ReturnType<typeof useDataGridEvents>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FillsManager = ({
|
export const FillsManager = ({
|
||||||
partyId,
|
partyId,
|
||||||
marketId,
|
marketId,
|
||||||
onMarketClick,
|
onMarketClick,
|
||||||
storeKey,
|
gridProps,
|
||||||
}: FillsManagerProps) => {
|
}: FillsManagerProps) => {
|
||||||
const gridRef = useRef<AgGridReact | null>(null);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
const filter: Schema.TradesFilter | Schema.TradesSubscriptionFilter = {
|
const filter: Schema.TradesFilter | Schema.TradesSubscriptionFilter = {
|
||||||
@ -38,9 +38,6 @@ export const FillsManager = ({
|
|||||||
},
|
},
|
||||||
variables: { filter },
|
variables: { filter },
|
||||||
});
|
});
|
||||||
const bottomPlaceholderProps = useBottomPlaceholder({
|
|
||||||
gridRef,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FillsTable
|
<FillsTable
|
||||||
@ -48,9 +45,8 @@ export const FillsManager = ({
|
|||||||
rowData={data}
|
rowData={data}
|
||||||
partyId={partyId}
|
partyId={partyId}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
storeKey={storeKey}
|
|
||||||
{...bottomPlaceholderProps}
|
|
||||||
overlayNoRowsTemplate={error ? error.message : t('No fills')}
|
overlayNoRowsTemplate={error ? error.message : t('No fills')}
|
||||||
|
{...gridProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -39,7 +39,6 @@ export type Role = typeof TAKER | typeof MAKER | '-';
|
|||||||
export type Props = (AgGridReactProps | AgReactUiProps) & {
|
export type Props = (AgGridReactProps | AgReactUiProps) & {
|
||||||
partyId: string;
|
partyId: string;
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
storeKey?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FillsTable = forwardRef<AgGridReact, Props>(
|
export const FillsTable = forwardRef<AgGridReact, Props>(
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
export * from './lib/ledger-container';
|
|
||||||
export * from './lib/ledger-manager';
|
export * from './lib/ledger-manager';
|
||||||
export * from './lib/__generated__/LedgerEntries';
|
export * from './lib/__generated__/LedgerEntries';
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
|
||||||
import { LedgerManager } from './ledger-manager';
|
|
||||||
|
|
||||||
export const LedgerContainer = () => {
|
|
||||||
const { pubKey } = useVegaWallet();
|
|
||||||
if (!pubKey) {
|
|
||||||
return (
|
|
||||||
<Splash>
|
|
||||||
<p>{t('Please connect Vega wallet')}</p>
|
|
||||||
</Splash>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <LedgerManager partyId={pubKey} />;
|
|
||||||
};
|
|
@ -10,6 +10,7 @@ import { LedgerTable } from './ledger-table';
|
|||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
import type * as Types from '@vegaprotocol/types';
|
import type * as Types from '@vegaprotocol/types';
|
||||||
import { LedgerExportLink } from './ledger-export-link';
|
import { LedgerExportLink } from './ledger-export-link';
|
||||||
|
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
|
|
||||||
export interface Filter {
|
export interface Filter {
|
||||||
vegaTime?: {
|
vegaTime?: {
|
||||||
@ -24,7 +25,13 @@ const defaultFilter = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LedgerManager = ({ partyId }: { partyId: string }) => {
|
export const LedgerManager = ({
|
||||||
|
partyId,
|
||||||
|
gridProps,
|
||||||
|
}: {
|
||||||
|
partyId: string;
|
||||||
|
gridProps: ReturnType<typeof useDataGridEvents>;
|
||||||
|
}) => {
|
||||||
const gridRef = useRef<AgGridReact | null>(null);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
const [filter, setFilter] = useState<Filter>(defaultFilter);
|
const [filter, setFilter] = useState<Filter>(defaultFilter);
|
||||||
|
|
||||||
@ -33,7 +40,7 @@ export const LedgerManager = ({ partyId }: { partyId: string }) => {
|
|||||||
partyId,
|
partyId,
|
||||||
dateRange: filter?.vegaTime?.value,
|
dateRange: filter?.vegaTime?.value,
|
||||||
pagination: {
|
pagination: {
|
||||||
first: 5000,
|
first: 10,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[partyId, filter?.vegaTime?.value]
|
[partyId, filter?.vegaTime?.value]
|
||||||
@ -45,18 +52,23 @@ export const LedgerManager = ({ partyId }: { partyId: string }) => {
|
|||||||
skip: !variables.partyId,
|
skip: !variables.partyId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const onFilterChanged = useCallback((event: FilterChangedEvent) => {
|
const onFilterChanged = useCallback(
|
||||||
|
(event: FilterChangedEvent) => {
|
||||||
const updatedFilter = { ...defaultFilter, ...event.api.getFilterModel() };
|
const updatedFilter = { ...defaultFilter, ...event.api.getFilterModel() };
|
||||||
setFilter(updatedFilter);
|
setFilter(updatedFilter);
|
||||||
}, []);
|
gridProps.onFilterChanged(event);
|
||||||
|
},
|
||||||
|
[gridProps]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full relative">
|
<div className="h-full relative">
|
||||||
<LedgerTable
|
<LedgerTable
|
||||||
ref={gridRef}
|
ref={gridRef}
|
||||||
rowData={data}
|
rowData={data}
|
||||||
onFilterChanged={onFilterChanged}
|
|
||||||
overlayNoRowsTemplate={error ? error.message : t('No entries')}
|
overlayNoRowsTemplate={error ? error.message : t('No entries')}
|
||||||
|
{...gridProps}
|
||||||
|
onFilterChanged={onFilterChanged}
|
||||||
/>
|
/>
|
||||||
{data && <LedgerExportLink entries={data} partyId={partyId} />}
|
{data && <LedgerExportLink entries={data} partyId={partyId} />}
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,7 +49,7 @@ export const LedgerTable = forwardRef<AgGridReact, LedgerEntryProps>(
|
|||||||
(props, ref) => {
|
(props, ref) => {
|
||||||
return (
|
return (
|
||||||
<AgGrid
|
<AgGrid
|
||||||
style={{ width: '100%', height: 'calc(100% - 50px)' }}
|
style={{ width: '100%', height: '100%' }}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
tooltipShowDelay={500}
|
tooltipShowDelay={500}
|
||||||
defaultColDef={{
|
defaultColDef={{
|
||||||
@ -61,9 +61,6 @@ export const LedgerTable = forwardRef<AgGridReact, LedgerEntryProps>(
|
|||||||
buttons: ['reset'],
|
buttons: ['reset'],
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
storeKey="ledgerTable"
|
|
||||||
suppressLoadingOverlay
|
|
||||||
suppressNoRowsOverlay
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
|
@ -197,7 +197,6 @@ export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
|
|||||||
tooltipComponent: TooltipCellComponent,
|
tooltipComponent: TooltipCellComponent,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
}}
|
}}
|
||||||
storeKey="liquidityProvisionTable"
|
|
||||||
{...props}
|
{...props}
|
||||||
columnDefs={colDefs}
|
columnDefs={colDefs}
|
||||||
/>
|
/>
|
||||||
|
@ -61,7 +61,6 @@ export const MarketListTable = forwardRef<
|
|||||||
columnDefs={columnDefs}
|
columnDefs={columnDefs}
|
||||||
suppressCellFocus
|
suppressCellFocus
|
||||||
components={{ PriceFlashCell, MarketName }}
|
components={{ PriceFlashCell, MarketName }}
|
||||||
storeKey="allMarkets"
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
export * from './order-data-provider';
|
export * from './order-data-provider';
|
||||||
export * from './order-list';
|
export * from './order-list';
|
||||||
export * from './order-list-manager';
|
export * from './order-list-manager';
|
||||||
export * from './order-list-container';
|
|
||||||
export * from './mocks/generate-orders';
|
export * from './mocks/generate-orders';
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
|
||||||
import type { Filter } from './order-list-manager';
|
|
||||||
import { OrderListManager } from './order-list-manager';
|
|
||||||
|
|
||||||
export interface OrderListContainerProps {
|
|
||||||
marketId?: string;
|
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
|
||||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
|
||||||
enforceBottomPlaceholder?: boolean;
|
|
||||||
filter?: Filter;
|
|
||||||
storeKey?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const OrderListContainer = ({
|
|
||||||
marketId,
|
|
||||||
onMarketClick,
|
|
||||||
onOrderTypeClick,
|
|
||||||
enforceBottomPlaceholder,
|
|
||||||
filter,
|
|
||||||
storeKey,
|
|
||||||
}: OrderListContainerProps) => {
|
|
||||||
const { pubKey, isReadOnly } = useVegaWallet();
|
|
||||||
|
|
||||||
if (!pubKey) {
|
|
||||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<OrderListManager
|
|
||||||
partyId={pubKey}
|
|
||||||
marketId={marketId}
|
|
||||||
filter={filter}
|
|
||||||
onMarketClick={onMarketClick}
|
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
|
||||||
isReadOnly={isReadOnly}
|
|
||||||
enforceBottomPlaceholder={enforceBottomPlaceholder}
|
|
||||||
storeKey={storeKey}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -2,11 +2,9 @@ import { t } from '@vegaprotocol/i18n';
|
|||||||
import { useCallback, useRef, useState } from 'react';
|
import { useCallback, useRef, useState } from 'react';
|
||||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { AgGridReact } from 'ag-grid-react';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
import type { GridReadyEvent, FilterChangedEvent } from 'ag-grid-community';
|
|
||||||
|
|
||||||
import { OrderListTable } from '../order-list/order-list';
|
import { OrderListTable } from '../order-list/order-list';
|
||||||
import { useHasAmendableOrder } from '../../order-hooks/use-has-amendable-order';
|
import { useHasAmendableOrder } from '../../order-hooks/use-has-amendable-order';
|
||||||
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
|
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
import { ordersWithMarketProvider } from '../order-data-provider/order-data-provider';
|
import { ordersWithMarketProvider } from '../order-data-provider/order-data-provider';
|
||||||
import {
|
import {
|
||||||
@ -16,59 +14,31 @@ import {
|
|||||||
import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/wallet';
|
import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/wallet';
|
||||||
import { OrderEditDialog } from '../order-list/order-edit-dialog';
|
import { OrderEditDialog } from '../order-list/order-edit-dialog';
|
||||||
import type { Order } from '../order-data-provider';
|
import type { Order } from '../order-data-provider';
|
||||||
import { OrderStatus } from '@vegaprotocol/types';
|
|
||||||
|
|
||||||
export enum Filter {
|
export enum Filter {
|
||||||
'Open',
|
'Open' = 'Open',
|
||||||
'Closed',
|
'Closed' = 'Closed',
|
||||||
'Rejected',
|
'Rejected' = 'Rejected',
|
||||||
}
|
}
|
||||||
|
|
||||||
const FilterStatusValue = {
|
|
||||||
[Filter.Open]: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
|
||||||
[Filter.Closed]: [
|
|
||||||
OrderStatus.STATUS_CANCELLED,
|
|
||||||
OrderStatus.STATUS_EXPIRED,
|
|
||||||
OrderStatus.STATUS_FILLED,
|
|
||||||
OrderStatus.STATUS_PARTIALLY_FILLED,
|
|
||||||
OrderStatus.STATUS_STOPPED,
|
|
||||||
],
|
|
||||||
[Filter.Rejected]: [OrderStatus.STATUS_REJECTED],
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface OrderListManagerProps {
|
export interface OrderListManagerProps {
|
||||||
partyId: string;
|
partyId: string;
|
||||||
marketId?: string;
|
marketId?: string;
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
enforceBottomPlaceholder?: boolean;
|
|
||||||
filter?: Filter;
|
filter?: Filter;
|
||||||
storeKey?: string;
|
gridProps?: ReturnType<typeof useDataGridEvents>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
|
|
||||||
<div className="dark:bg-black/75 bg-white/75 h-auto flex justify-end px-[11px] py-2 absolute bottom-0 right-3 rounded">
|
|
||||||
<Button
|
|
||||||
variant="primary"
|
|
||||||
size="sm"
|
|
||||||
onClick={onClick}
|
|
||||||
data-testid="cancelAll"
|
|
||||||
>
|
|
||||||
{t('Cancel all')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const OrderListManager = ({
|
export const OrderListManager = ({
|
||||||
partyId,
|
partyId,
|
||||||
marketId,
|
marketId,
|
||||||
onMarketClick,
|
onMarketClick,
|
||||||
onOrderTypeClick,
|
onOrderTypeClick,
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
enforceBottomPlaceholder,
|
|
||||||
filter,
|
filter,
|
||||||
storeKey,
|
gridProps,
|
||||||
}: OrderListManagerProps) => {
|
}: OrderListManagerProps) => {
|
||||||
const gridRef = useRef<AgGridReact | null>(null);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
const [editOrder, setEditOrder] = useState<Order | null>(null);
|
const [editOrder, setEditOrder] = useState<Order | null>(null);
|
||||||
@ -91,14 +61,6 @@ export const OrderListManager = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
|
||||||
onFilterChanged: bottomPlaceholderOnFilterChanged,
|
|
||||||
...bottomPlaceholderProps
|
|
||||||
} = useBottomPlaceholder({
|
|
||||||
gridRef,
|
|
||||||
disabled: !enforceBottomPlaceholder && !isReadOnly && !hasAmendableOrder,
|
|
||||||
});
|
|
||||||
|
|
||||||
const cancel = useCallback(
|
const cancel = useCallback(
|
||||||
(order: Order) => {
|
(order: Order) => {
|
||||||
if (!order.market) return;
|
if (!order.market) return;
|
||||||
@ -112,26 +74,6 @@ export const OrderListManager = ({
|
|||||||
[create]
|
[create]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onGridReady = useCallback(
|
|
||||||
({ api }: GridReadyEvent) => {
|
|
||||||
if (filter !== undefined) {
|
|
||||||
api.setFilterModel({
|
|
||||||
status: {
|
|
||||||
value: FilterStatusValue[filter],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[filter]
|
|
||||||
);
|
|
||||||
|
|
||||||
const onFilterChanged = useCallback(
|
|
||||||
(event: FilterChangedEvent) => {
|
|
||||||
bottomPlaceholderOnFilterChanged?.();
|
|
||||||
},
|
|
||||||
[bottomPlaceholderOnFilterChanged]
|
|
||||||
);
|
|
||||||
|
|
||||||
const cancelAll = useCallback(() => {
|
const cancelAll = useCallback(() => {
|
||||||
create({
|
create({
|
||||||
orderCancellation: {},
|
orderCancellation: {},
|
||||||
@ -142,20 +84,17 @@ export const OrderListManager = ({
|
|||||||
<>
|
<>
|
||||||
<div className="h-full relative">
|
<div className="h-full relative">
|
||||||
<OrderListTable
|
<OrderListTable
|
||||||
rowData={data as Order[]}
|
rowData={data}
|
||||||
ref={gridRef}
|
ref={gridRef}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
onGridReady={onGridReady}
|
|
||||||
onCancel={cancel}
|
onCancel={cancel}
|
||||||
onEdit={setEditOrder}
|
onEdit={setEditOrder}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
onFilterChanged={onFilterChanged}
|
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
storeKey={storeKey}
|
|
||||||
suppressAutoSize
|
suppressAutoSize
|
||||||
overlayNoRowsTemplate={error ? error.message : t('No orders')}
|
overlayNoRowsTemplate={error ? error.message : t('No orders')}
|
||||||
{...bottomPlaceholderProps}
|
{...gridProps}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{!isReadOnly && hasAmendableOrder && (
|
{!isReadOnly && hasAmendableOrder && (
|
||||||
@ -198,3 +137,16 @@ export const OrderListManager = ({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
|
||||||
|
<div className="dark:bg-black/75 bg-white/75 h-auto flex justify-end px-[11px] py-2 absolute bottom-0 right-3 rounded">
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
onClick={onClick}
|
||||||
|
data-testid="cancelAll"
|
||||||
|
>
|
||||||
|
{t('Cancel all')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
@ -39,7 +39,6 @@ export type OrderListTableProps = TypedDataAgGrid<Order> & {
|
|||||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
filter?: Filter;
|
filter?: Filter;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
storeKey?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const OrderListTable = memo<
|
export const OrderListTable = memo<
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
export * from './lib/__generated__/Positions';
|
export * from './lib/__generated__/Positions';
|
||||||
export * from './lib/positions-container';
|
|
||||||
export * from './lib/positions-data-providers';
|
export * from './lib/positions-data-providers';
|
||||||
export * from './lib/positions-table';
|
export * from './lib/positions-table';
|
||||||
|
export * from './lib/positions-manager';
|
||||||
export * from './lib/use-market-margin';
|
export * from './lib/use-market-margin';
|
||||||
export * from './lib/use-open-volume';
|
export * from './lib/use-open-volume';
|
||||||
|
@ -8,22 +8,21 @@ import {
|
|||||||
positionsMetricsProvider,
|
positionsMetricsProvider,
|
||||||
positionsMarketsProvider,
|
positionsMarketsProvider,
|
||||||
} from './positions-data-providers';
|
} from './positions-data-providers';
|
||||||
|
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
interface PositionsManagerProps {
|
interface PositionsManagerProps {
|
||||||
partyIds: string[];
|
partyIds: string[];
|
||||||
onMarketClick?: (marketId: string) => void;
|
onMarketClick?: (marketId: string) => void;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
noBottomPlaceholder?: boolean;
|
gridProps: ReturnType<typeof useDataGridEvents>;
|
||||||
storeKey?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PositionsManager = ({
|
export const PositionsManager = ({
|
||||||
partyIds,
|
partyIds,
|
||||||
onMarketClick,
|
onMarketClick,
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
noBottomPlaceholder,
|
gridProps,
|
||||||
storeKey,
|
|
||||||
}: PositionsManagerProps) => {
|
}: PositionsManagerProps) => {
|
||||||
const { pubKeys, pubKey } = useVegaWallet();
|
const { pubKeys, pubKey } = useVegaWallet();
|
||||||
const create = useVegaTransactionStore((store) => store.create);
|
const create = useVegaTransactionStore((store) => store.create);
|
||||||
@ -74,9 +73,9 @@ export const PositionsManager = ({
|
|||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
storeKey={storeKey}
|
|
||||||
multipleKeys={partyIds.length > 1}
|
multipleKeys={partyIds.length > 1}
|
||||||
overlayNoRowsTemplate={error ? error.message : t('No positions')}
|
overlayNoRowsTemplate={error ? error.message : t('No positions')}
|
||||||
|
{...gridProps}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -48,7 +48,6 @@ interface Props extends TypedDataAgGrid<Position> {
|
|||||||
onMarketClick?: (id: string, metaKey?: boolean) => void;
|
onMarketClick?: (id: string, metaKey?: boolean) => void;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
storeKey?: string;
|
|
||||||
multipleKeys?: boolean;
|
multipleKeys?: boolean;
|
||||||
pubKeys?: VegaWalletContextShape['pubKeys'];
|
pubKeys?: VegaWalletContextShape['pubKeys'];
|
||||||
pubKey?: VegaWalletContextShape['pubKey'];
|
pubKey?: VegaWalletContextShape['pubKey'];
|
||||||
|
@ -38,7 +38,6 @@ export const ProposalsList = () => {
|
|||||||
columnDefs={columnDefs}
|
columnDefs={columnDefs}
|
||||||
rowData={filteredData}
|
rowData={filteredData}
|
||||||
defaultColDef={defaultColDef}
|
defaultColDef={defaultColDef}
|
||||||
storeKey="proposedMarkets"
|
|
||||||
getRowId={({ data }) => data.id}
|
getRowId={({ data }) => data.id}
|
||||||
style={{ width: '100%', height: '100%' }}
|
style={{ width: '100%', height: '100%' }}
|
||||||
overlayNoRowsTemplate={error ? error.message : t('No markets')}
|
overlayNoRowsTemplate={error ? error.message : t('No markets')}
|
||||||
|
@ -55,7 +55,6 @@ export const TradesTable = forwardRef<AgGridReact, Props>((props, ref) => {
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
defaultColDef={{
|
defaultColDef={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
resizable: true,
|
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
@ -8,7 +8,6 @@ import {
|
|||||||
isNumeric,
|
isNumeric,
|
||||||
truncateByChars,
|
truncateByChars,
|
||||||
} from '@vegaprotocol/utils';
|
} from '@vegaprotocol/utils';
|
||||||
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import {
|
import {
|
||||||
ButtonLink,
|
ButtonLink,
|
||||||
@ -47,11 +46,10 @@ export const WithdrawalsTable = (
|
|||||||
(store) => store.create
|
(store) => store.create
|
||||||
);
|
);
|
||||||
|
|
||||||
const bottomPlaceholderProps = useBottomPlaceholder({ gridRef });
|
|
||||||
return (
|
return (
|
||||||
<AgGrid
|
<AgGrid
|
||||||
overlayNoRowsTemplate={t('No withdrawals')}
|
overlayNoRowsTemplate={t('No withdrawals')}
|
||||||
defaultColDef={{ resizable: true }}
|
defaultColDef={{ flex: 1 }}
|
||||||
style={{ width: '100%', height: '100%' }}
|
style={{ width: '100%', height: '100%' }}
|
||||||
components={{
|
components={{
|
||||||
RecipientCell,
|
RecipientCell,
|
||||||
@ -61,8 +59,6 @@ export const WithdrawalsTable = (
|
|||||||
}}
|
}}
|
||||||
suppressCellFocus
|
suppressCellFocus
|
||||||
ref={gridRef}
|
ref={gridRef}
|
||||||
storeKey="withdrawals"
|
|
||||||
{...bottomPlaceholderProps}
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<AgGridColumn headerName="Asset" field="asset.symbol" />
|
<AgGridColumn headerName="Asset" field="asset.symbol" />
|
||||||
|
Loading…
Reference in New Issue
Block a user