feat(trading): divide order tab into open, closed, rejected and all (#3541)
This commit is contained in:
parent
768b3b29f0
commit
c37f9ebe66
@ -9,6 +9,21 @@ describe('market bottom panel', { tags: '@smoke' }, () => {
|
|||||||
|
|
||||||
it('on xxl screen should be splitted out into two tables', () => {
|
it('on xxl screen should be splitted out into two tables', () => {
|
||||||
cy.getByTestId('tab-positions').should('have.attr', 'data-state', 'active');
|
cy.getByTestId('tab-positions').should('have.attr', 'data-state', 'active');
|
||||||
|
cy.getByTestId('tab-open-orders').should(
|
||||||
|
'have.attr',
|
||||||
|
'data-state',
|
||||||
|
'inactive'
|
||||||
|
);
|
||||||
|
cy.getByTestId('tab-closed-orders').should(
|
||||||
|
'have.attr',
|
||||||
|
'data-state',
|
||||||
|
'inactive'
|
||||||
|
);
|
||||||
|
cy.getByTestId('tab-rejected-orders').should(
|
||||||
|
'have.attr',
|
||||||
|
'data-state',
|
||||||
|
'inactive'
|
||||||
|
);
|
||||||
cy.getByTestId('tab-orders').should('have.attr', 'data-state', 'inactive');
|
cy.getByTestId('tab-orders').should('have.attr', 'data-state', 'inactive');
|
||||||
cy.getByTestId('tab-fills').should('have.attr', 'data-state', 'inactive');
|
cy.getByTestId('tab-fills').should('have.attr', 'data-state', 'inactive');
|
||||||
cy.getByTestId('tab-accounts').should(
|
cy.getByTestId('tab-accounts').should(
|
||||||
@ -19,7 +34,22 @@ describe('market bottom panel', { tags: '@smoke' }, () => {
|
|||||||
|
|
||||||
cy.viewport(1801, 1000);
|
cy.viewport(1801, 1000);
|
||||||
cy.getByTestId('tab-positions').should('have.attr', 'data-state', 'active');
|
cy.getByTestId('tab-positions').should('have.attr', 'data-state', 'active');
|
||||||
cy.getByTestId('tab-orders').should('have.attr', 'data-state', 'active');
|
cy.getByTestId('tab-open-orders').should(
|
||||||
|
'have.attr',
|
||||||
|
'data-state',
|
||||||
|
'inactive'
|
||||||
|
);
|
||||||
|
cy.getByTestId('tab-closed-orders').should(
|
||||||
|
'have.attr',
|
||||||
|
'data-state',
|
||||||
|
'inactive'
|
||||||
|
);
|
||||||
|
cy.getByTestId('tab-rejected-orders').should(
|
||||||
|
'have.attr',
|
||||||
|
'data-state',
|
||||||
|
'inactive'
|
||||||
|
);
|
||||||
|
cy.getByTestId('tab-orders').should('have.attr', 'data-state', 'inactive');
|
||||||
cy.getByTestId('tab-fills').should('have.attr', 'data-state', 'inactive');
|
cy.getByTestId('tab-fills').should('have.attr', 'data-state', 'inactive');
|
||||||
cy.getByTestId('tab-accounts').should(
|
cy.getByTestId('tab-accounts').should(
|
||||||
'have.attr',
|
'have.attr',
|
||||||
|
@ -29,7 +29,7 @@ describe('orders list', { tags: '@smoke', testIsolation: true }, () => {
|
|||||||
cy.mockSubscription(subscriptionMocks);
|
cy.mockSubscription(subscriptionMocks);
|
||||||
cy.setVegaWallet();
|
cy.setVegaWallet();
|
||||||
cy.visit('/#/markets/market-0');
|
cy.visit('/#/markets/market-0');
|
||||||
cy.getByTestId('Orders').click();
|
cy.getByTestId('All').click();
|
||||||
cy.wait('@Markets');
|
cy.wait('@Markets');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ describe('orders list', { tags: '@smoke', testIsolation: true }, () => {
|
|||||||
cy.get('[col-id="status"] .ag-icon-menu').click();
|
cy.get('[col-id="status"] .ag-icon-menu').click();
|
||||||
});
|
});
|
||||||
cy.contains('Partially Filled').click();
|
cy.contains('Partially Filled').click();
|
||||||
cy.getByTestId('Orders').click();
|
cy.getByTestId('All').click();
|
||||||
|
|
||||||
cy.get(`[row-id="${partiallyFilledId}"]`)
|
cy.get(`[row-id="${partiallyFilledId}"]`)
|
||||||
.eq(1)
|
.eq(1)
|
||||||
@ -116,7 +116,7 @@ describe('orders list', { tags: '@smoke', testIsolation: true }, () => {
|
|||||||
cy.get('[col-id="status"] .ag-icon-menu').click();
|
cy.get('[col-id="status"] .ag-icon-menu').click();
|
||||||
});
|
});
|
||||||
cy.contains('Reset').click();
|
cy.contains('Reset').click();
|
||||||
cy.getByTestId('Orders').click();
|
cy.getByTestId('All').click();
|
||||||
|
|
||||||
cy.getByTestId('tab-orders')
|
cy.getByTestId('tab-orders')
|
||||||
.get(`.ag-center-cols-container [col-id='${orderSymbol}']`)
|
.get(`.ag-center-cols-container [col-id='${orderSymbol}']`)
|
||||||
@ -139,14 +139,15 @@ describe('orders list', { tags: '@smoke', testIsolation: true }, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('subscribe orders', { tags: '@smoke' }, () => {
|
describe('subscribe orders', { tags: '@smoke' }, () => {
|
||||||
before(() => {
|
let orderId = '0';
|
||||||
|
beforeEach(() => {
|
||||||
const subscriptionMocks = getSubscriptionMocks();
|
const subscriptionMocks = getSubscriptionMocks();
|
||||||
cy.spy(subscriptionMocks, 'OrdersUpdate');
|
cy.spy(subscriptionMocks, 'OrdersUpdate');
|
||||||
cy.mockTradingPage();
|
cy.mockTradingPage();
|
||||||
cy.mockSubscription(subscriptionMocks);
|
cy.mockSubscription(subscriptionMocks);
|
||||||
cy.setVegaWallet();
|
cy.setVegaWallet();
|
||||||
cy.visit('/#/markets/market-0');
|
cy.visit('/#/markets/market-0');
|
||||||
cy.getByTestId('Orders').click();
|
cy.getByTestId('All').click();
|
||||||
cy.getByTestId('tab-orders').within(() => {
|
cy.getByTestId('tab-orders').within(() => {
|
||||||
cy.get('[col-id="status"][role="columnheader"]')
|
cy.get('[col-id="status"][role="columnheader"]')
|
||||||
.focus()
|
.focus()
|
||||||
@ -154,8 +155,8 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
|
|||||||
.click();
|
.click();
|
||||||
cy.get('.ag-filter-apply-panel-button').click();
|
cy.get('.ag-filter-apply-panel-button').click();
|
||||||
});
|
});
|
||||||
|
orderId = (parseInt(orderId, 10) + 1).toString();
|
||||||
});
|
});
|
||||||
const orderId = '1234567890';
|
|
||||||
// 7002-SORD-053
|
// 7002-SORD-053
|
||||||
// 7002-SORD-040
|
// 7002-SORD-040
|
||||||
// 7003-MORD-001
|
// 7003-MORD-001
|
||||||
@ -299,7 +300,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
|
|||||||
});
|
});
|
||||||
cy.get(`[row-id=${orderId}]`)
|
cy.get(`[row-id=${orderId}]`)
|
||||||
.find('[col-id="price"]')
|
.find('[col-id="price"]')
|
||||||
.should('have.text', '200.00');
|
.should('have.text', '-');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('must see the time in force applied to the order', () => {
|
it('must see the time in force applied to the order', () => {
|
||||||
@ -370,7 +371,7 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
|
|||||||
cy.mockSubscription(subscriptionMocks);
|
cy.mockSubscription(subscriptionMocks);
|
||||||
cy.setVegaWallet();
|
cy.setVegaWallet();
|
||||||
cy.visit('/#/markets/market-0');
|
cy.visit('/#/markets/market-0');
|
||||||
cy.getByTestId('Orders').click();
|
cy.getByTestId('All').click();
|
||||||
cy.getByTestId('tab-orders').within(() => {
|
cy.getByTestId('tab-orders').within(() => {
|
||||||
cy.get('[col-id="status"][role="columnheader"]')
|
cy.get('[col-id="status"][role="columnheader"]')
|
||||||
.focus()
|
.focus()
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
|
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
|
||||||
import { MarketInfoAccordionContainer } from '@vegaprotocol/market-info';
|
import { MarketInfoAccordionContainer } from '@vegaprotocol/market-info';
|
||||||
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
||||||
import { OrderListContainer } from '@vegaprotocol/orders';
|
import { OrderListContainer, Filter } from '@vegaprotocol/orders';
|
||||||
|
import type { OrderListContainerProps } from '@vegaprotocol/orders';
|
||||||
import { FillsContainer } from '@vegaprotocol/fills';
|
import { FillsContainer } from '@vegaprotocol/fills';
|
||||||
import { PositionsContainer } from '@vegaprotocol/positions';
|
import { PositionsContainer } from '@vegaprotocol/positions';
|
||||||
import { TradesContainer } from '@vegaprotocol/trades';
|
import { TradesContainer } from '@vegaprotocol/trades';
|
||||||
@ -58,17 +59,59 @@ const requiresMarket = (View: MarketDependantView) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const TradingViews = {
|
const TradingViews = {
|
||||||
Candles: requiresMarket(CandlesChartContainer),
|
candles: {
|
||||||
Depth: requiresMarket(DepthChartContainer),
|
label: 'Candles',
|
||||||
Liquidity: requiresMarket(LiquidityContainer),
|
component: requiresMarket(CandlesChartContainer),
|
||||||
Ticket: requiresMarket(DealTicketContainer),
|
},
|
||||||
Info: requiresMarket(MarketInfoAccordionContainer),
|
depth: {
|
||||||
Orderbook: requiresMarket(OrderbookContainer),
|
label: 'Depth',
|
||||||
Trades: requiresMarket(TradesContainer),
|
component: requiresMarket(DepthChartContainer),
|
||||||
Positions: PositionsContainer,
|
},
|
||||||
Orders: OrderListContainer,
|
liquidity: {
|
||||||
Collateral: AccountsContainer,
|
label: 'Liquidity',
|
||||||
Fills: FillsContainer,
|
component: requiresMarket(LiquidityContainer),
|
||||||
|
},
|
||||||
|
ticket: {
|
||||||
|
label: 'Ticket',
|
||||||
|
component: requiresMarket(DealTicketContainer),
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
label: 'Info',
|
||||||
|
component: requiresMarket(MarketInfoAccordionContainer),
|
||||||
|
},
|
||||||
|
orderbook: {
|
||||||
|
label: 'Orderbook',
|
||||||
|
component: requiresMarket(OrderbookContainer),
|
||||||
|
},
|
||||||
|
trades: {
|
||||||
|
label: 'Trades',
|
||||||
|
component: requiresMarket(TradesContainer),
|
||||||
|
},
|
||||||
|
positions: { label: 'Positions', component: PositionsContainer },
|
||||||
|
activeOrders: {
|
||||||
|
label: 'Active',
|
||||||
|
component: (props: OrderListContainerProps) => (
|
||||||
|
<OrderListContainer {...props} filter={Filter.Open} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
closedOrders: {
|
||||||
|
label: 'Closed',
|
||||||
|
component: (props: OrderListContainerProps) => (
|
||||||
|
<OrderListContainer {...props} filter={Filter.Closed} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
rejectedOrders: {
|
||||||
|
label: 'Rejected',
|
||||||
|
component: (props: OrderListContainerProps) => (
|
||||||
|
<OrderListContainer {...props} filter={Filter.Rejected} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
orders: {
|
||||||
|
label: 'All',
|
||||||
|
component: OrderListContainer,
|
||||||
|
},
|
||||||
|
collateral: { label: 'Collateral', component: AccountsContainer },
|
||||||
|
fills: { label: 'Fills', component: FillsContainer },
|
||||||
};
|
};
|
||||||
|
|
||||||
type TradingView = keyof typeof TradingViews;
|
type TradingView = keyof typeof TradingViews;
|
||||||
@ -104,9 +147,42 @@ const MarketBottomPanel = memo(
|
|||||||
>
|
>
|
||||||
<TradeGridChild>
|
<TradeGridChild>
|
||||||
<Tabs storageKey="console-trade-grid-bottom-left">
|
<Tabs storageKey="console-trade-grid-bottom-left">
|
||||||
<Tab id="orders" name={t('Orders')}>
|
<Tab id="open-orders" name={t('Open')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<TradingViews.Orders
|
<TradingViews.orders.component
|
||||||
|
marketId={marketId}
|
||||||
|
filter={Filter.Open}
|
||||||
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
|
enforceBottomPlaceholder
|
||||||
|
/>
|
||||||
|
</VegaWalletContainer>
|
||||||
|
</Tab>
|
||||||
|
<Tab id="closed-orders" name={t('Closed')}>
|
||||||
|
<VegaWalletContainer>
|
||||||
|
<TradingViews.orders.component
|
||||||
|
marketId={marketId}
|
||||||
|
filter={Filter.Closed}
|
||||||
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
|
enforceBottomPlaceholder
|
||||||
|
/>
|
||||||
|
</VegaWalletContainer>
|
||||||
|
</Tab>
|
||||||
|
<Tab id="rejected-orders" name={t('Rejected')}>
|
||||||
|
<VegaWalletContainer>
|
||||||
|
<TradingViews.orders.component
|
||||||
|
marketId={marketId}
|
||||||
|
filter={Filter.Rejected}
|
||||||
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
|
enforceBottomPlaceholder
|
||||||
|
/>
|
||||||
|
</VegaWalletContainer>
|
||||||
|
</Tab>
|
||||||
|
<Tab id="orders" name={t('All')}>
|
||||||
|
<VegaWalletContainer>
|
||||||
|
<TradingViews.orders.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
@ -116,7 +192,7 @@ const MarketBottomPanel = memo(
|
|||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="fills" name={t('Fills')}>
|
<Tab id="fills" name={t('Fills')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<TradingViews.Fills
|
<TradingViews.fills.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
/>
|
/>
|
||||||
@ -134,7 +210,7 @@ const MarketBottomPanel = memo(
|
|||||||
<Tabs storageKey="console-trade-grid-bottom-right">
|
<Tabs storageKey="console-trade-grid-bottom-right">
|
||||||
<Tab id="positions" name={t('Positions')}>
|
<Tab id="positions" name={t('Positions')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<TradingViews.Positions
|
<TradingViews.positions.component
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
noBottomPlaceholder
|
noBottomPlaceholder
|
||||||
/>
|
/>
|
||||||
@ -142,7 +218,7 @@ const MarketBottomPanel = memo(
|
|||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="accounts" name={t('Collateral')}>
|
<Tab id="accounts" name={t('Collateral')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<TradingViews.Collateral
|
<TradingViews.collateral.component
|
||||||
pinnedAsset={pinnedAsset}
|
pinnedAsset={pinnedAsset}
|
||||||
noBottomPlaceholder
|
noBottomPlaceholder
|
||||||
hideButtons
|
hideButtons
|
||||||
@ -158,12 +234,45 @@ 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 onMarketClick={onMarketClick} />
|
<TradingViews.positions.component onMarketClick={onMarketClick} />
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="orders" name={t('Orders')}>
|
<Tab id="open-orders" name={t('Open')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<TradingViews.Orders
|
<TradingViews.orders.component
|
||||||
|
marketId={marketId}
|
||||||
|
filter={Filter.Open}
|
||||||
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
|
enforceBottomPlaceholder
|
||||||
|
/>
|
||||||
|
</VegaWalletContainer>
|
||||||
|
</Tab>
|
||||||
|
<Tab id="closed-orders" name={t('Closed')}>
|
||||||
|
<VegaWalletContainer>
|
||||||
|
<TradingViews.orders.component
|
||||||
|
marketId={marketId}
|
||||||
|
filter={Filter.Closed}
|
||||||
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
|
enforceBottomPlaceholder
|
||||||
|
/>
|
||||||
|
</VegaWalletContainer>
|
||||||
|
</Tab>
|
||||||
|
<Tab id="rejected-orders" name={t('Rejected')}>
|
||||||
|
<VegaWalletContainer>
|
||||||
|
<TradingViews.orders.component
|
||||||
|
marketId={marketId}
|
||||||
|
filter={Filter.Rejected}
|
||||||
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
|
enforceBottomPlaceholder
|
||||||
|
/>
|
||||||
|
</VegaWalletContainer>
|
||||||
|
</Tab>
|
||||||
|
<Tab id="orders" name={t('All')}>
|
||||||
|
<VegaWalletContainer>
|
||||||
|
<TradingViews.orders.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
@ -173,7 +282,7 @@ const MarketBottomPanel = memo(
|
|||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="fills" name={t('Fills')}>
|
<Tab id="fills" name={t('Fills')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<TradingViews.Fills
|
<TradingViews.fills.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
/>
|
/>
|
||||||
@ -181,7 +290,10 @@ const MarketBottomPanel = memo(
|
|||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="accounts" name={t('Collateral')}>
|
<Tab id="accounts" name={t('Collateral')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<TradingViews.Collateral pinnedAsset={pinnedAsset} hideButtons />
|
<TradingViews.collateral.component
|
||||||
|
pinnedAsset={pinnedAsset}
|
||||||
|
hideButtons
|
||||||
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
@ -221,13 +333,13 @@ const MainGrid = memo(
|
|||||||
<TradeGridChild>
|
<TradeGridChild>
|
||||||
<Tabs storageKey="console-trade-grid-main-left">
|
<Tabs storageKey="console-trade-grid-main-left">
|
||||||
<Tab id="chart" name={t('Chart')}>
|
<Tab id="chart" name={t('Chart')}>
|
||||||
<TradingViews.Candles marketId={marketId} />
|
<TradingViews.candles.component marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="depth" name={t('Depth')}>
|
<Tab id="depth" name={t('Depth')}>
|
||||||
<TradingViews.Depth marketId={marketId} />
|
<TradingViews.depth.component marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="liquidity" name={t('Liquidity')}>
|
<Tab id="liquidity" name={t('Liquidity')}>
|
||||||
<TradingViews.Liquidity marketId={marketId} />
|
<TradingViews.liquidity.component marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</TradeGridChild>
|
</TradeGridChild>
|
||||||
@ -240,13 +352,13 @@ const MainGrid = memo(
|
|||||||
<TradeGridChild>
|
<TradeGridChild>
|
||||||
<Tabs storageKey="console-trade-grid-main-center">
|
<Tabs storageKey="console-trade-grid-main-center">
|
||||||
<Tab id="ticket" name={t('Ticket')}>
|
<Tab id="ticket" name={t('Ticket')}>
|
||||||
<TradingViews.Ticket
|
<TradingViews.ticket.component
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
onClickCollateral={() => navigate('/portfolio')}
|
onClickCollateral={() => navigate('/portfolio')}
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="info" name={t('Info')}>
|
<Tab id="info" name={t('Info')}>
|
||||||
<TradingViews.Info marketId={marketId} />
|
<TradingViews.info.component marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</TradeGridChild>
|
</TradeGridChild>
|
||||||
@ -259,10 +371,10 @@ const MainGrid = memo(
|
|||||||
<TradeGridChild>
|
<TradeGridChild>
|
||||||
<Tabs storageKey="console-trade-grid-main-right">
|
<Tabs storageKey="console-trade-grid-main-right">
|
||||||
<Tab id="orderbook" name={t('Orderbook')}>
|
<Tab id="orderbook" name={t('Orderbook')}>
|
||||||
<TradingViews.Orderbook marketId={marketId} />
|
<TradingViews.orderbook.component marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="trades" name={t('Trades')}>
|
<Tab id="trades" name={t('Trades')}>
|
||||||
<TradingViews.Trades marketId={marketId} />
|
<TradingViews.trades.component marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</TradeGridChild>
|
</TradeGridChild>
|
||||||
@ -330,7 +442,7 @@ export const TradePanels = ({
|
|||||||
const onMarketClick = useMarketClickHandler(true);
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||||
|
|
||||||
const [view, setView] = useState<TradingView>('Candles');
|
const [view, setView] = useState<TradingView>('candles');
|
||||||
const renderView = () => {
|
const renderView = () => {
|
||||||
const Component = memo<{
|
const Component = memo<{
|
||||||
marketId: string;
|
marketId: string;
|
||||||
@ -339,7 +451,7 @@ export const TradePanels = ({
|
|||||||
onOrderTypeClick?: (marketId: string) => void;
|
onOrderTypeClick?: (marketId: string) => void;
|
||||||
onClickCollateral: () => void;
|
onClickCollateral: () => void;
|
||||||
pinnedAsset?: PinnedAsset;
|
pinnedAsset?: PinnedAsset;
|
||||||
}>(TradingViews[view]);
|
}>(TradingViews[view].component);
|
||||||
|
|
||||||
if (!Component) {
|
if (!Component) {
|
||||||
throw new Error(`No component for view: ${view}`);
|
throw new Error(`No component for view: ${view}`);
|
||||||
@ -388,7 +500,7 @@ export const TradePanels = ({
|
|||||||
className={className}
|
className={className}
|
||||||
key={key}
|
key={key}
|
||||||
>
|
>
|
||||||
{key}
|
{TradingViews[key as keyof typeof TradingViews].label}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -35,7 +35,7 @@ import { AppLoader, DynamicLoader } from '../components/app-loader';
|
|||||||
import { Navbar } from '../components/navbar';
|
import { Navbar } from '../components/navbar';
|
||||||
import { ENV } from '../lib/config';
|
import { ENV } from '../lib/config';
|
||||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
import { activeOrdersProvider } from '@vegaprotocol/orders';
|
import { activeOrdersProvider, allOrdersProvider } from '@vegaprotocol/orders';
|
||||||
import { useTelemetryApproval } from '../lib/hooks/use-telemetry-approval';
|
import { useTelemetryApproval } from '../lib/hooks/use-telemetry-approval';
|
||||||
import {
|
import {
|
||||||
ProtocolUpgradeCountdownMode,
|
ProtocolUpgradeCountdownMode,
|
||||||
@ -150,6 +150,11 @@ const PartyData = () => {
|
|||||||
variables,
|
variables,
|
||||||
skip,
|
skip,
|
||||||
});
|
});
|
||||||
|
useDataProvider({
|
||||||
|
dataProvider: allOrdersProvider,
|
||||||
|
variables,
|
||||||
|
skip,
|
||||||
|
});
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,9 +37,9 @@ export const AccountManager = ({
|
|||||||
variables,
|
variables,
|
||||||
});
|
});
|
||||||
const setId = useCallback(
|
const setId = useCallback(
|
||||||
(data: AccountFields) => ({
|
(data: AccountFields, id: string) => ({
|
||||||
...data,
|
...data,
|
||||||
asset: { ...data.asset, id: `${data.asset.id}-1` },
|
asset: { ...data.asset, id },
|
||||||
}),
|
}),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
@ -126,14 +126,20 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
|||||||
return currentPinnedAssetRow;
|
return currentPinnedAssetRow;
|
||||||
}, [pinnedAssetId, props.pinnedAsset, props.rowData]);
|
}, [pinnedAssetId, props.pinnedAsset, props.rowData]);
|
||||||
|
|
||||||
const getRowHeight = useCallback(
|
const { getRowHeight } = props;
|
||||||
(params: RowHeightParams) =>
|
|
||||||
params.node.rowPinned &&
|
const getPinnedAssetRowHeight = useCallback(
|
||||||
params.data.asset.id === pinnedAssetId &&
|
(params: RowHeightParams) => {
|
||||||
new BigNumber(params.data.total).isLessThanOrEqualTo(0)
|
if (
|
||||||
? 32
|
params.node.rowPinned &&
|
||||||
: 24,
|
params.data.asset.id === pinnedAssetId &&
|
||||||
[pinnedAssetId]
|
new BigNumber(params.data.total).isLessThanOrEqualTo(0)
|
||||||
|
) {
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
return getRowHeight ? getRowHeight(params) : undefined;
|
||||||
|
},
|
||||||
|
[pinnedAssetId, getRowHeight]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -155,7 +161,7 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
comparator: accountValuesComparator,
|
comparator: accountValuesComparator,
|
||||||
}}
|
}}
|
||||||
getRowHeight={getRowHeight}
|
getRowHeight={getPinnedAssetRowHeight}
|
||||||
pinnedTopRowData={pinnedAssetRow ? [pinnedAssetRow] : undefined}
|
pinnedTopRowData={pinnedAssetRow ? [pinnedAssetRow] : undefined}
|
||||||
>
|
>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
|
@ -9,85 +9,90 @@ import {
|
|||||||
import type { IDoesFilterPassParams, IFilterParams } from 'ag-grid-community';
|
import type { IDoesFilterPassParams, IFilterParams } from 'ag-grid-community';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
export const SetFilter = forwardRef((props: IFilterParams, ref) => {
|
export const SetFilter = forwardRef(
|
||||||
const [value, setValue] = useState<string[]>([]);
|
(props: IFilterParams & { readonly?: boolean }, ref) => {
|
||||||
const valueRef = useRef(value);
|
const [value, setValue] = useState<string[]>([]);
|
||||||
|
const valueRef = useRef(value);
|
||||||
|
const { readonly } = props;
|
||||||
|
// expose AG Grid Filter Lifecycle callbacks
|
||||||
|
useImperativeHandle(ref, () => {
|
||||||
|
return {
|
||||||
|
doesFilterPass(params: IDoesFilterPassParams) {
|
||||||
|
const { api, colDef, column, columnApi, context } = props;
|
||||||
|
const { node } = params;
|
||||||
|
const getValue = props.valueGetter({
|
||||||
|
api,
|
||||||
|
colDef,
|
||||||
|
column,
|
||||||
|
columnApi,
|
||||||
|
context,
|
||||||
|
data: node.data,
|
||||||
|
getValue: (field) => node.data[field],
|
||||||
|
node,
|
||||||
|
});
|
||||||
|
return Array.isArray(value)
|
||||||
|
? value.includes(getValue)
|
||||||
|
: getValue === value;
|
||||||
|
},
|
||||||
|
|
||||||
// expose AG Grid Filter Lifecycle callbacks
|
isFilterActive() {
|
||||||
useImperativeHandle(ref, () => {
|
return valueRef.current.length !== 0;
|
||||||
return {
|
},
|
||||||
doesFilterPass(params: IDoesFilterPassParams) {
|
|
||||||
const { api, colDef, column, columnApi, context } = props;
|
|
||||||
const { node } = params;
|
|
||||||
const getValue = props.valueGetter({
|
|
||||||
api,
|
|
||||||
colDef,
|
|
||||||
column,
|
|
||||||
columnApi,
|
|
||||||
context,
|
|
||||||
data: node.data,
|
|
||||||
getValue: (field) => node.data[field],
|
|
||||||
node,
|
|
||||||
});
|
|
||||||
return Array.isArray(value)
|
|
||||||
? value.includes(getValue)
|
|
||||||
: getValue === value;
|
|
||||||
},
|
|
||||||
|
|
||||||
isFilterActive() {
|
getModel() {
|
||||||
return valueRef.current.length !== 0;
|
if (!this.isFilterActive()) {
|
||||||
},
|
return null;
|
||||||
|
}
|
||||||
|
return { value: valueRef.current };
|
||||||
|
},
|
||||||
|
|
||||||
getModel() {
|
setModel(model?: { value: string[] } | null) {
|
||||||
if (!this.isFilterActive()) {
|
valueRef.current = !model ? [] : model.value;
|
||||||
return null;
|
setValue(valueRef.current);
|
||||||
}
|
},
|
||||||
return { value: valueRef.current };
|
};
|
||||||
},
|
});
|
||||||
|
|
||||||
setModel(model?: { value: string[] } | null) {
|
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
valueRef.current = !model ? [] : model.value;
|
valueRef.current = event.target.checked
|
||||||
setValue(valueRef.current);
|
? [...value, event.target.value]
|
||||||
},
|
: value.filter((v) => v !== event.target.value);
|
||||||
|
setValue(valueRef.current);
|
||||||
};
|
};
|
||||||
});
|
|
||||||
|
|
||||||
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
|
useEffect(() => {
|
||||||
valueRef.current = event.target.checked
|
props.filterChangedCallback();
|
||||||
? [...value, event.target.value]
|
}, [value]); //eslint-disable-line react-hooks/exhaustive-deps
|
||||||
: value.filter((v) => v !== event.target.value);
|
return (
|
||||||
setValue(valueRef.current);
|
<div className="ag-filter-body-wrapper">
|
||||||
};
|
<fieldset className="ag-simple-filter-body-wrapper">
|
||||||
|
{Object.keys(props.colDef.filterParams.set).map((key) => (
|
||||||
useEffect(() => {
|
<label className="flex" key={key}>
|
||||||
props.filterChangedCallback();
|
<input
|
||||||
}, [value]); //eslint-disable-line react-hooks/exhaustive-deps
|
type="checkbox"
|
||||||
|
value={key}
|
||||||
return (
|
disabled={readonly}
|
||||||
<div className="ag-filter-body-wrapper">
|
className="mr-1"
|
||||||
<fieldset className="ag-simple-filter-body-wrapper">
|
checked={value.includes(key)}
|
||||||
{Object.keys(props.colDef.filterParams.set).map((key) => (
|
onChange={onChange}
|
||||||
<label className="flex" key={key}>
|
/>
|
||||||
<input
|
<span>{props.colDef.filterParams.set[key]}</span>
|
||||||
type="checkbox"
|
</label>
|
||||||
value={key}
|
))}
|
||||||
className="mr-1"
|
</fieldset>
|
||||||
checked={value.includes(key)}
|
{!readonly && (
|
||||||
onChange={onChange}
|
<div className="ag-filter-apply-panel">
|
||||||
/>
|
<button
|
||||||
<span>{props.colDef.filterParams.set[key]}</span>
|
type="button"
|
||||||
</label>
|
disabled={readonly}
|
||||||
))}
|
className="ag-standard-button ag-filter-apply-panel-button"
|
||||||
</fieldset>
|
onClick={() => setValue((valueRef.current = []))}
|
||||||
<div className="ag-filter-apply-panel">
|
>
|
||||||
<button
|
{t('Reset')}
|
||||||
type="button"
|
</button>
|
||||||
className="ag-standard-button ag-filter-apply-panel-button"
|
</div>
|
||||||
onClick={() => setValue((valueRef.current = []))}
|
)}
|
||||||
>
|
|
||||||
{t('Reset')}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
}
|
||||||
});
|
);
|
||||||
|
@ -62,7 +62,7 @@ export const FillsManager = ({
|
|||||||
scrolledToTop.current = event.top <= 0;
|
scrolledToTop.current = event.top <= 0;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { isFullWidthRow, fullWidthCellRenderer, rowClassRules } =
|
const { isFullWidthRow, fullWidthCellRenderer, rowClassRules, getRowHeight } =
|
||||||
useBottomPlaceholder<Trade>({
|
useBottomPlaceholder<Trade>({
|
||||||
gridRef,
|
gridRef,
|
||||||
});
|
});
|
||||||
@ -82,6 +82,7 @@ export const FillsManager = ({
|
|||||||
isFullWidthRow={isFullWidthRow}
|
isFullWidthRow={isFullWidthRow}
|
||||||
fullWidthCellRenderer={fullWidthCellRenderer}
|
fullWidthCellRenderer={fullWidthCellRenderer}
|
||||||
rowClassRules={rowClassRules}
|
rowClassRules={rowClassRules}
|
||||||
|
getRowHeight={getRowHeight}
|
||||||
/>
|
/>
|
||||||
<div className="pointer-events-none absolute inset-0">
|
<div className="pointer-events-none absolute inset-0">
|
||||||
<AsyncRenderer
|
<AsyncRenderer
|
||||||
|
@ -7,15 +7,14 @@ describe('order data provider', () => {
|
|||||||
const data = [
|
const data = [
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
id: '1',
|
id: '2',
|
||||||
updatedAt: new Date('2022-01-31').toISOString(),
|
|
||||||
createdAt: new Date('2022-01-29').toISOString(),
|
createdAt: new Date('2022-01-29').toISOString(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
id: '2',
|
id: '1',
|
||||||
createdAt: new Date('2022-01-30').toISOString(),
|
createdAt: new Date('2022-01-28').toISOString(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
] as Edge<OrderFieldsFragment>[];
|
] as Edge<OrderFieldsFragment>[];
|
||||||
@ -24,47 +23,51 @@ describe('order data provider', () => {
|
|||||||
// this one should be dropped because id don't exits and it's older than newest
|
// this one should be dropped because id don't exits and it's older than newest
|
||||||
{
|
{
|
||||||
id: '0',
|
id: '0',
|
||||||
createdAt: new Date('2022-01-30').toISOString(),
|
createdAt: new Date('2022-01-27').toISOString(),
|
||||||
},
|
},
|
||||||
// this one should be dropped because newer below
|
// this one should be dropped because newer below
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
updatedAt: new Date('2022-02-01').toISOString(),
|
updatedAt: new Date('2022-02-01').toISOString(),
|
||||||
createdAt: new Date('2022-01-29').toISOString(),
|
createdAt: new Date('2022-01-28').toISOString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
updatedAt: new Date('2022-02-02').toISOString(),
|
updatedAt: new Date('2022-02-04').toISOString(),
|
||||||
createdAt: new Date('2022-01-29').toISOString(),
|
createdAt: new Date('2022-01-28').toISOString(),
|
||||||
},
|
},
|
||||||
// this should be added
|
// this should be added
|
||||||
{
|
{
|
||||||
id: '4',
|
id: '4',
|
||||||
createdAt: new Date('2022-02-04').toISOString(),
|
createdAt: new Date('2022-02-04').toISOString(),
|
||||||
},
|
},
|
||||||
// this should be move to top
|
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
updatedAt: new Date('2022-02-03').toISOString(),
|
updatedAt: new Date('2022-02-04').toISOString(),
|
||||||
createdAt: new Date('2022-01-29').toISOString(),
|
createdAt: new Date('2022-01-30').toISOString(),
|
||||||
|
},
|
||||||
|
// this should be added
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
createdAt: new Date('2022-02-05').toISOString(),
|
||||||
},
|
},
|
||||||
] as OrderUpdateFieldsFragment[];
|
] as OrderUpdateFieldsFragment[];
|
||||||
|
|
||||||
const updatedData = update(data, delta, () => null, { partyId: '0x123' });
|
const updatedData = update(data, delta, () => null, { partyId: '0x123' });
|
||||||
expect(
|
expect(
|
||||||
updatedData?.findIndex((edge) => edge.node.id === delta[0].id)
|
updatedData?.findIndex((edge) => edge.node.id === delta[0].id)
|
||||||
).toEqual(-1);
|
).toEqual(-1);
|
||||||
expect(updatedData && updatedData[2].node.id).toEqual(delta[2].id);
|
expect(updatedData && updatedData[3].node.id).toEqual(delta[2].id);
|
||||||
expect(updatedData && updatedData[2].node.updatedAt).toEqual(
|
expect(updatedData && updatedData[3].node.updatedAt).toEqual(
|
||||||
delta[2].updatedAt
|
delta[2].updatedAt
|
||||||
);
|
);
|
||||||
expect(updatedData && updatedData[0].node.id).toEqual(delta[3].id);
|
expect(updatedData && updatedData[0].node.id).toEqual(delta[5].id);
|
||||||
expect(updatedData && updatedData[1].node.id).toEqual(delta[4].id);
|
expect(updatedData && updatedData[1].node.id).toEqual(delta[3].id);
|
||||||
expect(updatedData && updatedData[1].node.updatedAt).toEqual(
|
expect(updatedData && updatedData[2].node.id).toEqual(delta[4].id);
|
||||||
|
expect(updatedData && updatedData[2].node.updatedAt).toEqual(
|
||||||
delta[4].updatedAt
|
delta[4].updatedAt
|
||||||
);
|
);
|
||||||
expect(update([], delta, () => null, { partyId: '0x123' })?.length).toEqual(
|
expect(update([], delta, () => null, { partyId: '0x123' })?.length).toEqual(
|
||||||
4
|
5
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('add only data matching date range filter', () => {
|
it('add only data matching date range filter', () => {
|
||||||
@ -72,7 +75,6 @@ describe('order data provider', () => {
|
|||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
id: '1',
|
id: '1',
|
||||||
updatedAt: new Date('2022-01-31').toISOString(),
|
|
||||||
createdAt: new Date('2022-01-29').toISOString(),
|
createdAt: new Date('2022-01-29').toISOString(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -90,12 +92,6 @@ describe('order data provider', () => {
|
|||||||
id: '0',
|
id: '0',
|
||||||
createdAt: new Date('2022-02-02').toISOString(),
|
createdAt: new Date('2022-02-02').toISOString(),
|
||||||
},
|
},
|
||||||
// this one should be removed because it does not match date range
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
updatedAt: new Date('2022-02-02').toISOString(),
|
|
||||||
createdAt: new Date('2022-01-29').toISOString(),
|
|
||||||
},
|
|
||||||
// this one should be updated
|
// this one should be updated
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
@ -118,16 +114,13 @@ describe('order data provider', () => {
|
|||||||
expect(
|
expect(
|
||||||
updatedData?.findIndex((edge) => edge.node.id === delta[0].id)
|
updatedData?.findIndex((edge) => edge.node.id === delta[0].id)
|
||||||
).toEqual(-1);
|
).toEqual(-1);
|
||||||
expect(
|
|
||||||
updatedData?.findIndex((edge) => edge.node.id === delta[1].id)
|
|
||||||
).toEqual(-1);
|
|
||||||
expect(updatedData && updatedData[0].node.id).toEqual(delta[2].id);
|
expect(updatedData && updatedData[0].node.id).toEqual(delta[2].id);
|
||||||
expect(updatedData && updatedData[0].node.updatedAt).toEqual(
|
expect(updatedData && updatedData[0].node.updatedAt).toEqual(
|
||||||
delta[2].updatedAt
|
delta[2].updatedAt
|
||||||
);
|
);
|
||||||
expect(updatedData && updatedData[1].node.id).toEqual(delta[3].id);
|
expect(updatedData && updatedData[2].node.id).toEqual(delta[1].id);
|
||||||
expect(updatedData && updatedData[1].node.updatedAt).toEqual(
|
expect(updatedData && updatedData[2].node.updatedAt).toEqual(
|
||||||
delta[3].updatedAt
|
delta[1].updatedAt
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -5,8 +5,6 @@ import {
|
|||||||
makeDataProvider,
|
makeDataProvider,
|
||||||
makeDerivedDataProvider,
|
makeDerivedDataProvider,
|
||||||
defaultAppend as append,
|
defaultAppend as append,
|
||||||
paginatedCombineDelta as combineDelta,
|
|
||||||
paginatedCombineInsertionData as combineInsertionData,
|
|
||||||
} from '@vegaprotocol/utils';
|
} from '@vegaprotocol/utils';
|
||||||
import type { Market } from '@vegaprotocol/market-list';
|
import type { Market } from '@vegaprotocol/market-list';
|
||||||
import { marketsProvider } from '@vegaprotocol/market-list';
|
import { marketsProvider } from '@vegaprotocol/market-list';
|
||||||
@ -28,6 +26,11 @@ export type Order = Omit<OrderFieldsFragment, 'market'> & {
|
|||||||
};
|
};
|
||||||
export type OrderEdge = Edge<Order>;
|
export type OrderEdge = Edge<Order>;
|
||||||
|
|
||||||
|
const liveOnlyOrderStatuses = [
|
||||||
|
OrderStatus.STATUS_ACTIVE,
|
||||||
|
OrderStatus.STATUS_PARKED,
|
||||||
|
];
|
||||||
|
|
||||||
const orderMatchFilters = (
|
const orderMatchFilters = (
|
||||||
order: OrderUpdateFieldsFragment,
|
order: OrderUpdateFieldsFragment,
|
||||||
variables: OrdersQueryVariables
|
variables: OrdersQueryVariables
|
||||||
@ -41,6 +44,12 @@ const orderMatchFilters = (
|
|||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
variables?.filter?.liveOnly &&
|
||||||
|
!(order.status && liveOnlyOrderStatuses.includes(order.status))
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
variables?.filter?.types &&
|
variables?.filter?.types &&
|
||||||
!(order.type && variables.filter.types.includes(order.type))
|
!(order.type && variables.filter.types.includes(order.type))
|
||||||
@ -58,19 +67,13 @@ const orderMatchFilters = (
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
variables?.filter?.dateRange?.start &&
|
variables?.filter?.dateRange?.start &&
|
||||||
!(
|
!(order.createdAt && variables.filter.dateRange.start < order.createdAt)
|
||||||
(order.updatedAt || order.createdAt) &&
|
|
||||||
variables.filter.dateRange.start < (order.updatedAt || order.createdAt)
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
variables?.filter?.dateRange?.end &&
|
variables?.filter?.dateRange?.end &&
|
||||||
!(
|
!(order.createdAt && variables.filter.dateRange.end > order.createdAt)
|
||||||
(order.updatedAt || order.createdAt) &&
|
|
||||||
variables.filter.dateRange.end > (order.updatedAt || order.createdAt)
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -120,28 +123,25 @@ export const update = (
|
|||||||
if (!data) {
|
if (!data) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
return produce(data, (draft) => {
|
// A single update can contain the same order with multiple updates, so we need to find
|
||||||
// A single update can contain the same order with multiple updates, so we need to find
|
// the latest version of the order and only update using that
|
||||||
// the latest version of the order and only update using that
|
const incoming = orderBy(
|
||||||
const incoming = uniqBy(
|
uniqBy(
|
||||||
orderBy(delta, (order) => order.updatedAt || order.createdAt, 'desc'),
|
orderBy(delta, (order) => order.updatedAt || order.createdAt, 'desc'),
|
||||||
'id'
|
'id'
|
||||||
);
|
),
|
||||||
|
'createdAt'
|
||||||
|
);
|
||||||
|
return produce(data, (draft) => {
|
||||||
// Add or update incoming orders
|
// Add or update incoming orders
|
||||||
incoming.reverse().forEach((node) => {
|
incoming.forEach((node) => {
|
||||||
const index = draft.findIndex((edge) => edge.node.id === node.id);
|
const index = draft.findIndex((edge) => edge.node.id === node.id);
|
||||||
const newer =
|
const newer =
|
||||||
draft.length === 0 ||
|
draft.length === 0 || node.createdAt >= draft[0].node.createdAt;
|
||||||
(node.updatedAt || node.createdAt) >=
|
|
||||||
(draft[0].node.updatedAt || draft[0].node.createdAt);
|
|
||||||
const doesFilterPass = !variables || orderMatchFilters(node, variables);
|
const doesFilterPass = !variables || orderMatchFilters(node, variables);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
if (doesFilterPass) {
|
if (doesFilterPass) {
|
||||||
Object.assign(draft[index].node, node);
|
Object.assign(draft[index].node, node);
|
||||||
if (newer) {
|
|
||||||
draft.unshift(...draft.splice(index, 1));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
draft.splice(index, 1);
|
draft.splice(index, 1);
|
||||||
}
|
}
|
||||||
@ -194,6 +194,34 @@ const ordersProvider = makeDataProvider<
|
|||||||
additionalContext: { isEnlargedTimeout: true },
|
additionalContext: { isEnlargedTimeout: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const allOrderMaxCount = 50000;
|
||||||
|
|
||||||
|
export const allOrdersProvider = makeDerivedDataProvider<
|
||||||
|
ReturnType<typeof getData>,
|
||||||
|
never,
|
||||||
|
{ partyId: string; marketId?: string }
|
||||||
|
>(
|
||||||
|
[
|
||||||
|
(callback, client, variables) =>
|
||||||
|
ordersProvider(callback, client, { partyId: variables.partyId }),
|
||||||
|
],
|
||||||
|
(partsData, variables, prevData, parts, subscriptions) => {
|
||||||
|
const orders = partsData[0] as ReturnType<typeof getData>;
|
||||||
|
// load next pages until allOrderMaxCount reached
|
||||||
|
if (
|
||||||
|
!parts[0].isUpdate &&
|
||||||
|
subscriptions &&
|
||||||
|
subscriptions[0].load &&
|
||||||
|
orders?.length < allOrderMaxCount
|
||||||
|
) {
|
||||||
|
subscriptions[0].load();
|
||||||
|
}
|
||||||
|
return variables.marketId
|
||||||
|
? orders.filter((edge) => variables.marketId === edge.node.market.id)
|
||||||
|
: orders;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export const activeOrdersProvider = makeDerivedDataProvider<
|
export const activeOrdersProvider = makeDerivedDataProvider<
|
||||||
ReturnType<typeof getData>,
|
ReturnType<typeof getData>,
|
||||||
never,
|
never,
|
||||||
@ -204,11 +232,12 @@ export const activeOrdersProvider = makeDerivedDataProvider<
|
|||||||
ordersProvider(callback, client, {
|
ordersProvider(callback, client, {
|
||||||
partyId: variables.partyId,
|
partyId: variables.partyId,
|
||||||
filter: {
|
filter: {
|
||||||
status: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
liveOnly: true,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
(partsData, variables, prevData, parts, subscriptions) => {
|
(partsData, variables, prevData, parts, subscriptions) => {
|
||||||
|
// load all pages
|
||||||
if (!parts[0].isUpdate && subscriptions && subscriptions[0].load) {
|
if (!parts[0].isUpdate && subscriptions && subscriptions[0].load) {
|
||||||
subscriptions[0].load();
|
subscriptions[0].load();
|
||||||
}
|
}
|
||||||
@ -220,7 +249,7 @@ export const activeOrdersProvider = makeDerivedDataProvider<
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const ordersWithMarketProvider = makeDerivedDataProvider<
|
export const ordersWithMarketProvider = makeDerivedDataProvider<
|
||||||
(OrderEdge | null)[],
|
(Order | null)[],
|
||||||
Order[],
|
Order[],
|
||||||
OrdersQueryVariables
|
OrdersQueryVariables
|
||||||
>(
|
>(
|
||||||
@ -228,18 +257,13 @@ export const ordersWithMarketProvider = makeDerivedDataProvider<
|
|||||||
ordersProvider,
|
ordersProvider,
|
||||||
(callback, client) => marketsProvider(callback, client, undefined),
|
(callback, client) => marketsProvider(callback, client, undefined),
|
||||||
],
|
],
|
||||||
(partsData): OrderEdge[] =>
|
(partsData): Order[] =>
|
||||||
((partsData[0] as ReturnType<typeof getData>) || []).map((edge) => ({
|
((partsData[0] as ReturnType<typeof getData>) || []).map((edge) => ({
|
||||||
cursor: edge.cursor,
|
...edge.node,
|
||||||
node: {
|
market: (partsData[1] as Market[]).find(
|
||||||
...edge.node,
|
(market) => market.id === edge.node.market.id
|
||||||
market: (partsData[1] as Market[]).find(
|
),
|
||||||
(market) => market.id === edge.node.market.id
|
}))
|
||||||
),
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
combineDelta<Order, ReturnType<typeof getDelta>['0']>,
|
|
||||||
combineInsertionData<Order>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export const hasActiveOrderProvider = makeDerivedDataProvider<
|
export const hasActiveOrderProvider = makeDerivedDataProvider<
|
||||||
|
@ -2,18 +2,23 @@ import { t } from '@vegaprotocol/i18n';
|
|||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { OrderListManager } from './order-list-manager';
|
import { OrderListManager } from './order-list-manager';
|
||||||
|
import type { Filter } 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;
|
||||||
|
}
|
||||||
|
|
||||||
export const OrderListContainer = ({
|
export const OrderListContainer = ({
|
||||||
marketId,
|
marketId,
|
||||||
onMarketClick,
|
onMarketClick,
|
||||||
onOrderTypeClick,
|
onOrderTypeClick,
|
||||||
enforceBottomPlaceholder,
|
enforceBottomPlaceholder,
|
||||||
}: {
|
filter,
|
||||||
marketId?: string;
|
}: OrderListContainerProps) => {
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
|
||||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
|
||||||
enforceBottomPlaceholder?: boolean;
|
|
||||||
}) => {
|
|
||||||
const { pubKey, isReadOnly } = useVegaWallet();
|
const { pubKey, isReadOnly } = useVegaWallet();
|
||||||
|
|
||||||
if (!pubKey) {
|
if (!pubKey) {
|
||||||
@ -24,6 +29,7 @@ export const OrderListContainer = ({
|
|||||||
<OrderListManager
|
<OrderListManager
|
||||||
partyId={pubKey}
|
partyId={pubKey}
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
|
filter={filter}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
|
@ -1,2 +1 @@
|
|||||||
export * from './order-list-manager';
|
export * from './order-list-manager';
|
||||||
export * from './use-order-list-data';
|
|
||||||
|
@ -1,25 +1,41 @@
|
|||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import type { FilterChangedEvent, SortChangedEvent } from 'ag-grid-community';
|
|
||||||
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 } from 'ag-grid-community';
|
import type { GridReadyEvent, FilterChangedEvent } from 'ag-grid-community';
|
||||||
|
|
||||||
import { OrderListTable } from '../order-list/order-list';
|
import { OrderListTable } from '../order-list/order-list';
|
||||||
import { useOrderListData } from './use-order-list-data';
|
|
||||||
import { useHasAmendableOrder } from '../../order-hooks/use-has-amendable-order';
|
import { useHasAmendableOrder } from '../../order-hooks/use-has-amendable-order';
|
||||||
import type { Filter, Sort } from './use-order-list-data';
|
|
||||||
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
|
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
|
||||||
import { OrderStatus } from '@vegaprotocol/types';
|
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
|
import { ordersWithMarketProvider } from '../order-data-provider/order-data-provider';
|
||||||
import {
|
import {
|
||||||
normalizeOrderAmendment,
|
normalizeOrderAmendment,
|
||||||
useVegaTransactionStore,
|
useVegaTransactionStore,
|
||||||
} from '@vegaprotocol/wallet';
|
} from '@vegaprotocol/wallet';
|
||||||
import isEqual from 'lodash/isEqual';
|
|
||||||
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, OrderEdge } from '../order-data-provider';
|
import type { Order } from '../order-data-provider';
|
||||||
|
import { OrderStatus } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
export enum Filter {
|
||||||
|
'Open',
|
||||||
|
'Closed',
|
||||||
|
'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;
|
||||||
@ -28,6 +44,7 @@ export interface OrderListManagerProps {
|
|||||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
enforceBottomPlaceholder?: boolean;
|
enforceBottomPlaceholder?: boolean;
|
||||||
|
filter?: Filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
|
const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
|
||||||
@ -43,12 +60,6 @@ const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const initialFilter: Filter = {
|
|
||||||
status: {
|
|
||||||
value: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const OrderListManager = ({
|
export const OrderListManager = ({
|
||||||
partyId,
|
partyId,
|
||||||
marketId,
|
marketId,
|
||||||
@ -56,27 +67,22 @@ export const OrderListManager = ({
|
|||||||
onOrderTypeClick,
|
onOrderTypeClick,
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
enforceBottomPlaceholder,
|
enforceBottomPlaceholder,
|
||||||
|
filter,
|
||||||
}: OrderListManagerProps) => {
|
}: OrderListManagerProps) => {
|
||||||
const gridRef = useRef<AgGridReact | null>(null);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
const [dataCount, setDataCount] = useState(0);
|
const [hasData, setHasData] = useState(false);
|
||||||
const scrolledToTop = useRef(false);
|
|
||||||
const [sort, setSort] = useState<Sort[] | undefined>();
|
|
||||||
const [filter, setFilter] = useState<Filter | undefined>(initialFilter);
|
|
||||||
const filterRef = useRef(initialFilter);
|
|
||||||
const [editOrder, setEditOrder] = useState<Order | null>(null);
|
const [editOrder, setEditOrder] = useState<Order | null>(null);
|
||||||
const create = useVegaTransactionStore((state) => state.create);
|
const create = useVegaTransactionStore((state) => state.create);
|
||||||
const hasAmendableOrder = useHasAmendableOrder(marketId);
|
const hasAmendableOrder = useHasAmendableOrder(marketId);
|
||||||
|
const { data, error, loading, reload } = useDataProvider({
|
||||||
const { data, error, loading, reload } = useOrderListData({
|
dataProvider: ordersWithMarketProvider,
|
||||||
partyId,
|
variables:
|
||||||
sort,
|
filter === Filter.Open
|
||||||
filter,
|
? { partyId, filter: { liveOnly: true } }
|
||||||
gridRef,
|
: { partyId },
|
||||||
scrolledToTop,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
onSortChanged: bottomPlaceholderOnSortChanged,
|
|
||||||
onFilterChanged: bottomPlaceholderOnFilterChanged,
|
onFilterChanged: bottomPlaceholderOnFilterChanged,
|
||||||
...bottomPlaceholderProps
|
...bottomPlaceholderProps
|
||||||
} = useBottomPlaceholder<Order>({
|
} = useBottomPlaceholder<Order>({
|
||||||
@ -84,42 +90,6 @@ export const OrderListManager = ({
|
|||||||
disabled: !enforceBottomPlaceholder && !isReadOnly && !hasAmendableOrder,
|
disabled: !enforceBottomPlaceholder && !isReadOnly && !hasAmendableOrder,
|
||||||
});
|
});
|
||||||
|
|
||||||
const onFilterChanged = useCallback(
|
|
||||||
(event: FilterChangedEvent) => {
|
|
||||||
const updatedFilter = event.api.getFilterModel();
|
|
||||||
if (isEqual(updatedFilter, filterRef.current)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
filterRef.current = updatedFilter;
|
|
||||||
if (Object.keys(updatedFilter).length) {
|
|
||||||
setFilter(updatedFilter);
|
|
||||||
} else {
|
|
||||||
setFilter(undefined);
|
|
||||||
}
|
|
||||||
setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0);
|
|
||||||
bottomPlaceholderOnFilterChanged?.();
|
|
||||||
},
|
|
||||||
[setFilter, bottomPlaceholderOnFilterChanged]
|
|
||||||
);
|
|
||||||
|
|
||||||
const onSortChange = useCallback(
|
|
||||||
(event: SortChangedEvent) => {
|
|
||||||
const sort = event.columnApi
|
|
||||||
.getColumnState()
|
|
||||||
.sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0))
|
|
||||||
.reduce((acc, col) => {
|
|
||||||
if (col.sort) {
|
|
||||||
const { colId, sort } = col;
|
|
||||||
acc.push({ colId, sort });
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, [] as { colId: string; sort: string }[]);
|
|
||||||
setSort(sort.length > 0 ? sort : undefined);
|
|
||||||
bottomPlaceholderOnSortChanged?.();
|
|
||||||
},
|
|
||||||
[setSort, bottomPlaceholderOnSortChanged]
|
|
||||||
);
|
|
||||||
|
|
||||||
const cancel = useCallback(
|
const cancel = useCallback(
|
||||||
(order: Order) => {
|
(order: Order) => {
|
||||||
if (!order.market) return;
|
if (!order.market) return;
|
||||||
@ -133,12 +103,30 @@ export const OrderListManager = ({
|
|||||||
[create]
|
[create]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onGridReady = useCallback(({ api }: GridReadyEvent) => {
|
const onGridReady = useCallback(
|
||||||
api.setFilterModel(initialFilter);
|
({ api }: GridReadyEvent) => {
|
||||||
}, []);
|
if (filter !== undefined) {
|
||||||
|
api.setFilterModel({
|
||||||
|
status: {
|
||||||
|
value: FilterStatusValue[filter],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[filter]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onFilterChanged = useCallback(
|
||||||
|
(event: FilterChangedEvent) => {
|
||||||
|
const rowCount = gridRef.current?.api?.getModel().getRowCount();
|
||||||
|
setHasData((rowCount ?? 0) > 0);
|
||||||
|
bottomPlaceholderOnFilterChanged?.();
|
||||||
|
},
|
||||||
|
[bottomPlaceholderOnFilterChanged]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0);
|
setHasData((gridRef.current?.api?.getModel().getRowCount() ?? 0) > 0);
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
const cancelAll = useCallback(() => {
|
const cancelAll = useCallback(() => {
|
||||||
@ -148,26 +136,20 @@ export const OrderListManager = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [create, marketId]);
|
}, [create, marketId]);
|
||||||
const extractedData =
|
|
||||||
data && !loading
|
|
||||||
? data
|
|
||||||
.filter((item) => item !== null)
|
|
||||||
.map((item) => (item as OrderEdge).node)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="h-full relative">
|
<div className="h-full relative">
|
||||||
<OrderListTable
|
<OrderListTable
|
||||||
rowData={extractedData}
|
rowData={data as Order[]}
|
||||||
ref={gridRef}
|
ref={gridRef}
|
||||||
|
readonlyStatusFilter={filter !== undefined}
|
||||||
onGridReady={onGridReady}
|
onGridReady={onGridReady}
|
||||||
onFilterChanged={onFilterChanged}
|
|
||||||
onSortChanged={onSortChange}
|
|
||||||
cancel={cancel}
|
cancel={cancel}
|
||||||
setEditOrder={setEditOrder}
|
setEditOrder={setEditOrder}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
|
onFilterChanged={onFilterChanged}
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
blockLoadDebounceMillis={100}
|
blockLoadDebounceMillis={100}
|
||||||
suppressLoadingOverlay
|
suppressLoadingOverlay
|
||||||
@ -180,7 +162,7 @@ export const OrderListManager = ({
|
|||||||
error={error}
|
error={error}
|
||||||
data={data}
|
data={data}
|
||||||
noDataMessage={t('No orders')}
|
noDataMessage={t('No orders')}
|
||||||
noDataCondition={(data) => !dataCount}
|
noDataCondition={(data) => !hasData}
|
||||||
reload={reload}
|
reload={reload}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,166 +0,0 @@
|
|||||||
import type { AgGridReact } from 'ag-grid-react';
|
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
|
||||||
import { renderHook, waitFor } from '@testing-library/react';
|
|
||||||
import { useOrderListData } from './use-order-list-data';
|
|
||||||
import type { Edge } from '@vegaprotocol/utils';
|
|
||||||
import type { OrderFieldsFragment } from '../order-data-provider/__generated__/Orders';
|
|
||||||
import type { IGetRowsParams } from 'ag-grid-community';
|
|
||||||
|
|
||||||
const loadMock = jest.fn();
|
|
||||||
|
|
||||||
let mockData: Edge<OrderFieldsFragment>[] | null = null;
|
|
||||||
let mockDataProviderData = {
|
|
||||||
data: mockData as (Edge<OrderFieldsFragment> | null)[] | null,
|
|
||||||
error: undefined,
|
|
||||||
loading: true,
|
|
||||||
load: loadMock,
|
|
||||||
totalCount: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
let updateMock: jest.Mock;
|
|
||||||
const mockDataProvider = jest.fn((args) => {
|
|
||||||
updateMock = args.update;
|
|
||||||
return mockDataProviderData;
|
|
||||||
});
|
|
||||||
jest.mock('@vegaprotocol/react-helpers', () => ({
|
|
||||||
...jest.requireActual('@vegaprotocol/react-helpers'),
|
|
||||||
useDataProvider: jest.fn((args) => mockDataProvider(args)),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('useOrderListData Hook', () => {
|
|
||||||
const mockRefreshAgGridApi = jest.fn();
|
|
||||||
const partyId = 'partyId';
|
|
||||||
const gridRef = {
|
|
||||||
current: {
|
|
||||||
api: {
|
|
||||||
refreshInfiniteCache: mockRefreshAgGridApi,
|
|
||||||
getModel: () => ({ getType: () => 'infinite' }),
|
|
||||||
},
|
|
||||||
} as unknown as AgGridReact,
|
|
||||||
};
|
|
||||||
const scrolledToTop = {
|
|
||||||
current: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return proper dataProvider results', () => {
|
|
||||||
const { result } = renderHook(
|
|
||||||
() => useOrderListData({ partyId, gridRef, scrolledToTop }),
|
|
||||||
{
|
|
||||||
wrapper: MockedProvider,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(result.current).toMatchObject({
|
|
||||||
data: null,
|
|
||||||
error: undefined,
|
|
||||||
loading: true,
|
|
||||||
addNewRows: expect.any(Function),
|
|
||||||
getRows: expect.any(Function),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('return proper mocked results', () => {
|
|
||||||
mockData = [
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
id: 'data_id_1',
|
|
||||||
createdAt: 1,
|
|
||||||
},
|
|
||||||
} as unknown as Edge<OrderFieldsFragment>,
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
id: 'data_id_2',
|
|
||||||
createdAt: 2,
|
|
||||||
},
|
|
||||||
} as unknown as Edge<OrderFieldsFragment>,
|
|
||||||
];
|
|
||||||
mockDataProviderData = {
|
|
||||||
...mockDataProviderData,
|
|
||||||
data: mockData,
|
|
||||||
loading: false,
|
|
||||||
};
|
|
||||||
const { result } = renderHook(
|
|
||||||
() => useOrderListData({ partyId, gridRef, scrolledToTop }),
|
|
||||||
{
|
|
||||||
wrapper: MockedProvider,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(result.current).toMatchObject({
|
|
||||||
data: mockData,
|
|
||||||
error: undefined,
|
|
||||||
loading: false,
|
|
||||||
addNewRows: expect.any(Function),
|
|
||||||
getRows: expect.any(Function),
|
|
||||||
});
|
|
||||||
updateMock({ data: mockData, delta: [] });
|
|
||||||
expect(mockRefreshAgGridApi).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('methods for pagination should work', async () => {
|
|
||||||
const successCallback = jest.fn();
|
|
||||||
mockData = [
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
id: 'data_id_1',
|
|
||||||
createdAt: 1,
|
|
||||||
},
|
|
||||||
} as unknown as Edge<OrderFieldsFragment>,
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
id: 'data_id_2',
|
|
||||||
createdAt: 2,
|
|
||||||
},
|
|
||||||
} as unknown as Edge<OrderFieldsFragment>,
|
|
||||||
];
|
|
||||||
Object.assign(mockDataProviderData, {
|
|
||||||
data: mockData,
|
|
||||||
loading: false,
|
|
||||||
});
|
|
||||||
const mockDelta = [
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
id: 'data_id_3',
|
|
||||||
createdAt: 3,
|
|
||||||
},
|
|
||||||
} as unknown as Edge<OrderFieldsFragment>,
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
id: 'data_id_4',
|
|
||||||
createdAt: 4,
|
|
||||||
},
|
|
||||||
} as unknown as Edge<OrderFieldsFragment>,
|
|
||||||
];
|
|
||||||
const mockNextData = [...mockData, ...mockDelta];
|
|
||||||
const { result } = renderHook(
|
|
||||||
() => useOrderListData({ partyId, gridRef, scrolledToTop }),
|
|
||||||
{
|
|
||||||
wrapper: MockedProvider,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const getRowsParams = {
|
|
||||||
successCallback,
|
|
||||||
failCallback: jest.fn(),
|
|
||||||
startRow: 2,
|
|
||||||
endRow: 4,
|
|
||||||
} as unknown as IGetRowsParams;
|
|
||||||
|
|
||||||
await waitFor(async () => {
|
|
||||||
updateMock({ data: mockData });
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitFor(async () => {
|
|
||||||
const promise = result.current.getRows(getRowsParams);
|
|
||||||
updateMock({ data: mockNextData, delta: mockDelta });
|
|
||||||
await promise;
|
|
||||||
});
|
|
||||||
expect(loadMock).toHaveBeenCalled();
|
|
||||||
expect(successCallback).toHaveBeenLastCalledWith(
|
|
||||||
mockDelta.map((item) => item.node),
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,171 +0,0 @@
|
|||||||
import { useCallback, useMemo, useRef } from 'react';
|
|
||||||
import type { RefObject } from 'react';
|
|
||||||
import type { AgGridReact } from 'ag-grid-react';
|
|
||||||
import { makeInfiniteScrollGetRows } from '@vegaprotocol/utils';
|
|
||||||
import { useDataProvider, updateGridData } from '@vegaprotocol/react-helpers';
|
|
||||||
import { ordersWithMarketProvider } from '../order-data-provider/order-data-provider';
|
|
||||||
import type {
|
|
||||||
OrderEdge,
|
|
||||||
Order,
|
|
||||||
} from '../order-data-provider/order-data-provider';
|
|
||||||
import type {
|
|
||||||
OrdersQueryVariables,
|
|
||||||
OrdersUpdateSubscriptionVariables,
|
|
||||||
} from '../order-data-provider/__generated__/Orders';
|
|
||||||
import type * as Types from '@vegaprotocol/types';
|
|
||||||
export interface Sort {
|
|
||||||
colId: string;
|
|
||||||
sort: string;
|
|
||||||
}
|
|
||||||
export interface Filter {
|
|
||||||
updatedAt?: {
|
|
||||||
value: Types.DateRange;
|
|
||||||
};
|
|
||||||
type?: {
|
|
||||||
value: Types.OrderType[];
|
|
||||||
};
|
|
||||||
status?: {
|
|
||||||
value: Types.OrderStatus[];
|
|
||||||
};
|
|
||||||
timeInForce?: {
|
|
||||||
value: Types.OrderTimeInForce[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
interface Props {
|
|
||||||
partyId: string;
|
|
||||||
marketId?: string;
|
|
||||||
filter?: Filter;
|
|
||||||
sort?: Sort[];
|
|
||||||
gridRef: RefObject<AgGridReact>;
|
|
||||||
scrolledToTop: RefObject<boolean>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useOrderListData = ({
|
|
||||||
partyId,
|
|
||||||
marketId,
|
|
||||||
sort,
|
|
||||||
filter,
|
|
||||||
gridRef,
|
|
||||||
scrolledToTop,
|
|
||||||
}: Props) => {
|
|
||||||
const dataRef = useRef<(OrderEdge | null)[] | null>(null);
|
|
||||||
const totalCountRef = useRef<number | undefined>(undefined);
|
|
||||||
const newRows = useRef(0);
|
|
||||||
const placeholderAdded = useRef(-1);
|
|
||||||
|
|
||||||
const makeBottomPlaceholders = useCallback((order?: Order) => {
|
|
||||||
if (!order) {
|
|
||||||
if (placeholderAdded.current >= 0) {
|
|
||||||
dataRef.current?.splice(placeholderAdded.current, 1);
|
|
||||||
}
|
|
||||||
placeholderAdded.current = -1;
|
|
||||||
} else if (placeholderAdded.current === -1) {
|
|
||||||
dataRef.current?.push({
|
|
||||||
node: { ...order, id: `${order?.id}-1`, isLastPlaceholder: true },
|
|
||||||
});
|
|
||||||
placeholderAdded.current = (dataRef.current?.length || 0) - 1;
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const variables = useMemo(() => {
|
|
||||||
// define variable as const to get type safety, using generic with useMemo resulted in lost type safety
|
|
||||||
const allVars: OrdersQueryVariables & OrdersUpdateSubscriptionVariables = {
|
|
||||||
partyId,
|
|
||||||
};
|
|
||||||
if (
|
|
||||||
filter?.updatedAt?.value ||
|
|
||||||
filter?.status?.value.length ||
|
|
||||||
filter?.timeInForce?.value.length ||
|
|
||||||
filter?.type?.value.length
|
|
||||||
) {
|
|
||||||
allVars.filter = {};
|
|
||||||
if (filter?.updatedAt?.value) {
|
|
||||||
allVars.filter.dateRange = filter?.updatedAt?.value;
|
|
||||||
}
|
|
||||||
if (filter?.status?.value.length) {
|
|
||||||
allVars.filter.status = filter?.status?.value;
|
|
||||||
}
|
|
||||||
if (filter?.timeInForce?.value.length) {
|
|
||||||
allVars.filter.timeInForce = filter?.timeInForce?.value;
|
|
||||||
}
|
|
||||||
if (filter?.type?.value.length) {
|
|
||||||
allVars.filter.types = filter?.type?.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return allVars;
|
|
||||||
}, [partyId, filter]);
|
|
||||||
|
|
||||||
const addNewRows = useCallback(() => {
|
|
||||||
if (newRows.current === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (totalCountRef.current !== undefined) {
|
|
||||||
totalCountRef.current += newRows.current;
|
|
||||||
}
|
|
||||||
newRows.current = 0;
|
|
||||||
gridRef.current?.api?.refreshInfiniteCache();
|
|
||||||
}, [gridRef]);
|
|
||||||
|
|
||||||
const update = useCallback(
|
|
||||||
({
|
|
||||||
data,
|
|
||||||
delta,
|
|
||||||
}: {
|
|
||||||
data: (OrderEdge | null)[] | null;
|
|
||||||
delta?: Order[];
|
|
||||||
totalCount?: number;
|
|
||||||
}) => {
|
|
||||||
if (dataRef.current?.length && delta?.length && !scrolledToTop.current) {
|
|
||||||
const createdAt = dataRef.current?.[0]?.node.createdAt;
|
|
||||||
if (createdAt) {
|
|
||||||
newRows.current += (delta || []).filter(
|
|
||||||
(trade) => trade.createdAt > createdAt
|
|
||||||
).length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (gridRef.current?.api?.getModel().getType() === 'infinite') {
|
|
||||||
return updateGridData(dataRef, data, gridRef);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
[gridRef, scrolledToTop]
|
|
||||||
);
|
|
||||||
|
|
||||||
const insert = useCallback(
|
|
||||||
({
|
|
||||||
data,
|
|
||||||
totalCount,
|
|
||||||
}: {
|
|
||||||
data: (OrderEdge | null)[] | null;
|
|
||||||
totalCount?: number;
|
|
||||||
}) => {
|
|
||||||
totalCountRef.current = totalCount;
|
|
||||||
if (gridRef.current?.api?.getModel().getType() === 'infinite') {
|
|
||||||
return updateGridData(dataRef, data, gridRef);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
[gridRef]
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data, error, loading, load, totalCount, reload } = useDataProvider({
|
|
||||||
dataProvider: ordersWithMarketProvider,
|
|
||||||
update,
|
|
||||||
insert,
|
|
||||||
variables,
|
|
||||||
});
|
|
||||||
totalCountRef.current = totalCount;
|
|
||||||
|
|
||||||
const getRows = useRef(
|
|
||||||
makeInfiniteScrollGetRows<OrderEdge>(dataRef, totalCountRef, load, newRows)
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
loading,
|
|
||||||
error,
|
|
||||||
data,
|
|
||||||
addNewRows,
|
|
||||||
getRows: getRows.current,
|
|
||||||
reload,
|
|
||||||
makeBottomPlaceholders,
|
|
||||||
};
|
|
||||||
};
|
|
@ -33,13 +33,21 @@ export type OrderListTableProps = OrderListProps & {
|
|||||||
setEditOrder: (order: Order) => void;
|
setEditOrder: (order: Order) => void;
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
|
readonlyStatusFilter?: boolean;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const OrderListTable = memo(
|
export const OrderListTable = memo(
|
||||||
forwardRef<AgGridReact, OrderListTableProps>(
|
forwardRef<AgGridReact, OrderListTableProps>(
|
||||||
(
|
(
|
||||||
{ cancel, setEditOrder, onMarketClick, onOrderTypeClick, ...props },
|
{
|
||||||
|
cancel,
|
||||||
|
setEditOrder,
|
||||||
|
onMarketClick,
|
||||||
|
onOrderTypeClick,
|
||||||
|
readonlyStatusFilter,
|
||||||
|
...props
|
||||||
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
@ -119,6 +127,7 @@ export const OrderListTable = memo(
|
|||||||
filter={SetFilter}
|
filter={SetFilter}
|
||||||
filterParams={{
|
filterParams={{
|
||||||
set: Schema.OrderStatusMapping,
|
set: Schema.OrderStatusMapping,
|
||||||
|
readonly: readonlyStatusFilter,
|
||||||
}}
|
}}
|
||||||
valueFormatter={({
|
valueFormatter={({
|
||||||
value,
|
value,
|
||||||
@ -229,6 +238,7 @@ export const OrderListTable = memo(
|
|||||||
/>
|
/>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
field="createdAt"
|
field="createdAt"
|
||||||
|
filter={DateRangeFilter}
|
||||||
cellRenderer={({
|
cellRenderer={({
|
||||||
data,
|
data,
|
||||||
value,
|
value,
|
||||||
@ -243,7 +253,6 @@ export const OrderListTable = memo(
|
|||||||
/>
|
/>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
field="updatedAt"
|
field="updatedAt"
|
||||||
filter={DateRangeFilter}
|
|
||||||
cellRenderer={({
|
cellRenderer={({
|
||||||
data,
|
data,
|
||||||
value,
|
value,
|
||||||
|
@ -55,10 +55,10 @@ export const PositionsManager = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const setId = useCallback((data: Position) => {
|
const setId = useCallback((data: Position, id: string) => {
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
marketId: `${data.marketId}-1`,
|
marketId: id,
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
const bottomPlaceholderProps = useBottomPlaceholder<Position>({
|
const bottomPlaceholderProps = useBottomPlaceholder<Position>({
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import type { RefObject } from 'react';
|
import type { RefObject } from 'react';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import type { AgGridReact } from 'ag-grid-react';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
import type { IsFullWidthRowParams } from 'ag-grid-community';
|
import type { IsFullWidthRowParams, RowHeightParams } from 'ag-grid-community';
|
||||||
|
|
||||||
const NO_HOVER_CSS_RULE = { 'no-hover': 'data?.isLastPlaceholder' };
|
const NO_HOVER_CSS_RULE = { 'no-hover': 'data?.isLastPlaceholder' };
|
||||||
|
const ROW_ID = 'bottomPlaceholder';
|
||||||
const fullWidthCellRenderer = () => null;
|
const fullWidthCellRenderer = () => null;
|
||||||
const isFullWidthRow = (params: IsFullWidthRowParams) =>
|
const isFullWidthRow = (params: IsFullWidthRowParams) =>
|
||||||
params.rowNode.data?.isLastPlaceholder;
|
params.rowNode.data?.isLastPlaceholder;
|
||||||
|
|
||||||
interface Props<T> {
|
interface Props<T> {
|
||||||
gridRef: RefObject<AgGridReact>;
|
gridRef: RefObject<AgGridReact>;
|
||||||
setId?: (data: T) => T;
|
setId?: (data: T, id: string) => T;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
@ -20,45 +21,42 @@ export const useBottomPlaceholder = <T extends {}>({
|
|||||||
disabled,
|
disabled,
|
||||||
}: Props<T>) => {
|
}: Props<T>) => {
|
||||||
const onBodyScrollEnd = useCallback(() => {
|
const onBodyScrollEnd = useCallback(() => {
|
||||||
const rowCont = gridRef.current?.api.getModel().getRowCount() ?? 0;
|
const rowCont = gridRef.current?.api.getDisplayedRowCount() ?? 0;
|
||||||
const lastRowIndex = gridRef.current?.api.getLastDisplayedRow() ?? 0;
|
if (rowCont) {
|
||||||
if (lastRowIndex && rowCont - 1 === lastRowIndex) {
|
const lastRow = gridRef.current?.api.getDisplayedRowAtIndex(rowCont - 1);
|
||||||
const lastRow = gridRef.current?.api.getDisplayedRowAtIndex(lastRowIndex);
|
if (lastRow && lastRow.data) {
|
||||||
if (lastRow?.data && !lastRow?.data.isLastPlaceholder) {
|
const placeholderRow = setId
|
||||||
const newData = setId
|
? setId({ ...lastRow.data, isLastPlaceholder: true }, ROW_ID)
|
||||||
? setId({ ...lastRow.data, isLastPlaceholder: true })
|
|
||||||
: {
|
: {
|
||||||
...lastRow.data,
|
...lastRow.data,
|
||||||
isLastPlaceholder: true,
|
isLastPlaceholder: true,
|
||||||
id: `${lastRow.data?.id || '-'}-1`,
|
id: ROW_ID,
|
||||||
};
|
};
|
||||||
const add = [newData];
|
const transaction = gridRef.current?.api.getRowNode(ROW_ID)
|
||||||
const newIndex = lastRowIndex + 1;
|
? { update: [placeholderRow] }
|
||||||
gridRef.current?.api.applyTransaction({
|
: { add: [placeholderRow] };
|
||||||
add,
|
gridRef.current?.api.applyTransaction(transaction);
|
||||||
addIndex: newIndex,
|
|
||||||
});
|
|
||||||
const newLastRow =
|
|
||||||
gridRef.current?.api.getDisplayedRowAtIndex(newIndex);
|
|
||||||
newLastRow?.setRowHeight(50);
|
|
||||||
gridRef.current?.api.onRowHeightChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [gridRef, setId]);
|
}, [gridRef, setId]);
|
||||||
|
|
||||||
const onRowsChanged = useCallback(() => {
|
const onRowsChanged = useCallback(() => {
|
||||||
const remove: T[] = [];
|
const placeholderNode = gridRef.current?.api.getRowNode(ROW_ID);
|
||||||
gridRef.current?.api.forEachNodeAfterFilterAndSort((rowNode) => {
|
if (placeholderNode) {
|
||||||
if (rowNode.data.isLastPlaceholder) {
|
const transaction = {
|
||||||
remove.push(rowNode.data);
|
remove: [placeholderNode.data],
|
||||||
}
|
};
|
||||||
});
|
gridRef.current?.api.applyTransaction(transaction);
|
||||||
gridRef.current?.api.applyTransaction({
|
}
|
||||||
remove,
|
|
||||||
});
|
|
||||||
onBodyScrollEnd();
|
onBodyScrollEnd();
|
||||||
}, [gridRef, onBodyScrollEnd]);
|
}, [gridRef, onBodyScrollEnd]);
|
||||||
|
|
||||||
|
const getRowHeight = useCallback(
|
||||||
|
(params: RowHeightParams) =>
|
||||||
|
params.data?.isLastPlaceholder ? 50 : undefined,
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() =>
|
() =>
|
||||||
!disabled
|
!disabled
|
||||||
@ -69,8 +67,9 @@ export const useBottomPlaceholder = <T extends {}>({
|
|||||||
fullWidthCellRenderer,
|
fullWidthCellRenderer,
|
||||||
onSortChanged: onRowsChanged,
|
onSortChanged: onRowsChanged,
|
||||||
onFilterChanged: onRowsChanged,
|
onFilterChanged: onRowsChanged,
|
||||||
|
getRowHeight,
|
||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
[onBodyScrollEnd, onRowsChanged, disabled]
|
[onBodyScrollEnd, onRowsChanged, disabled, getRowHeight]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user