chore(orders): remove infinite scroll from order table (#3247)

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
Maciek 2023-03-23 09:13:07 +01:00 committed by GitHub
parent b611d30b31
commit f649d78565
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 108 additions and 83 deletions

View File

@ -22,7 +22,7 @@ const cancelAllOrdersBtn = 'cancelAll';
const editOrderBtn = 'edit'; const editOrderBtn = 'edit';
describe('orders list', { tags: '@smoke' }, () => { describe('orders list', { tags: '@smoke' }, () => {
before(() => { beforeEach(() => {
const subscriptionMocks = getSubscriptionMocks(); const subscriptionMocks = getSubscriptionMocks();
cy.spy(subscriptionMocks, 'OrdersUpdate'); cy.spy(subscriptionMocks, 'OrdersUpdate');
cy.mockTradingPage(); cy.mockTradingPage();
@ -82,6 +82,13 @@ describe('orders list', { tags: '@smoke' }, () => {
'94aead3ca92dc932efcb503631b03a410e2a5d4606cae6083e2406dc38e52f78'; '94aead3ca92dc932efcb503631b03a410e2a5d4606cae6083e2406dc38e52f78';
cy.getByTestId('tab-orders').should('be.visible'); cy.getByTestId('tab-orders').should('be.visible');
cy.get('.ag-header-container').within(() => {
cy.get('[col-id="status"]').realHover();
cy.get('[col-id="status"] .ag-icon-menu').click();
});
cy.contains('Partially Filled').click();
cy.getByTestId('Orders').click();
cy.get(`[row-id="${partiallyFilledId}"]`).within(() => { cy.get(`[row-id="${partiallyFilledId}"]`).within(() => {
cy.get(`[col-id='${orderStatus}']`).should( cy.get(`[col-id='${orderStatus}']`).should(
'have.text', 'have.text',
@ -102,6 +109,12 @@ describe('orders list', { tags: '@smoke' }, () => {
'BTCUSD.MF21', 'BTCUSD.MF21',
'BTCUSD.MF21', 'BTCUSD.MF21',
]; ];
cy.get('.ag-header-container').within(() => {
cy.get('[col-id="status"]').realHover();
cy.get('[col-id="status"] .ag-icon-menu').click();
});
cy.contains('Reset').click();
cy.getByTestId('Orders').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}']`)

View File

@ -102,6 +102,7 @@ const MarketBottomPanel = memo(
<TradingViews.Orders <TradingViews.Orders
marketId={marketId} marketId={marketId}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
enforceBottomPlaceholder
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
@ -157,6 +158,7 @@ const MarketBottomPanel = memo(
<TradingViews.Orders <TradingViews.Orders
marketId={marketId} marketId={marketId}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
enforceBottomPlaceholder
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>

View File

@ -6,9 +6,11 @@ import { OrderListManager } from './order-list-manager';
export const OrderListContainer = ({ export const OrderListContainer = ({
marketId, marketId,
onMarketClick, onMarketClick,
enforceBottomPlaceholder,
}: { }: {
marketId?: string; marketId?: string;
onMarketClick?: (marketId: string) => void; onMarketClick?: (marketId: string) => void;
enforceBottomPlaceholder?: boolean;
}) => { }) => {
const { pubKey, isReadOnly } = useVegaWallet(); const { pubKey, isReadOnly } = useVegaWallet();
@ -22,6 +24,7 @@ export const OrderListContainer = ({
marketId={marketId} marketId={marketId}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
isReadOnly={isReadOnly} isReadOnly={isReadOnly}
enforceBottomPlaceholder={enforceBottomPlaceholder}
/> />
); );
}; };

View File

@ -74,4 +74,20 @@ describe('OrderListManager', () => {
}); });
expect(await screen.findByText('OrderList')).toBeInTheDocument(); expect(await screen.findByText('OrderList')).toBeInTheDocument();
}); });
it('should show no orders message', async () => {
jest.spyOn(useDataProviderHook, 'useDataProvider').mockReturnValue({
data: [],
loading: false,
error: undefined,
flush: jest.fn(),
reload: jest.fn(),
load: jest.fn(),
totalCount: undefined,
});
await act(async () => {
render(generateJsx());
});
expect(screen.getByText('No orders')).toBeInTheDocument();
});
}); });

View File

@ -1,12 +1,7 @@
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useCallback, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import type { import type { FilterChangedEvent, SortChangedEvent } from 'ag-grid-community';
BodyScrollEvent,
BodyScrollEndEvent,
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 } from 'ag-grid-community';
@ -23,13 +18,14 @@ import {
} from '@vegaprotocol/wallet'; } from '@vegaprotocol/wallet';
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, OrderEdge } from '../order-data-provider';
export interface OrderListManagerProps { export interface OrderListManagerProps {
partyId: string; partyId: string;
marketId?: string; marketId?: string;
onMarketClick?: (marketId: string) => void; onMarketClick?: (marketId: string) => void;
isReadOnly: boolean; isReadOnly: boolean;
enforceBottomPlaceholder?: boolean;
} }
const CancelAllOrdersButton = ({ const CancelAllOrdersButton = ({
@ -65,78 +61,50 @@ export const OrderListManager = ({
marketId, marketId,
onMarketClick, onMarketClick,
isReadOnly, isReadOnly,
enforceBottomPlaceholder,
}: OrderListManagerProps) => { }: OrderListManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null); const gridRef = useRef<AgGridReact | null>(null);
const scrolledToTop = useRef(true); const [dataCount, setDataCount] = useState(0);
const scrolledToTop = useRef(false);
const [sort, setSort] = useState<Sort[] | undefined>(); const [sort, setSort] = useState<Sort[] | undefined>();
const [filter, setFilter] = useState<Filter | undefined>(initialFilter); const [filter, setFilter] = useState<Filter | undefined>(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 hasActiveOrder = useHasActiveOrder(marketId); const hasActiveOrder = useHasActiveOrder(marketId);
const { const { data, error, loading, reload } = useOrderListData({
data,
error,
loading,
addNewRows,
getRows,
reload,
makeBottomPlaceholders,
} = useOrderListData({
partyId, partyId,
marketId,
sort, sort,
filter, filter,
gridRef, gridRef,
scrolledToTop, scrolledToTop,
}); });
const checkBottomPlaceholder = useCallback(() => { const {
if (!isReadOnly && hasActiveOrder) { onSortChanged: bottomPlaceholderOnSortChanged,
const rowCont = gridRef.current?.api?.getModel().getRowCount() ?? 0; onFilterChanged: bottomPlaceholderOnFilterChanged,
const lastRowIndex = gridRef.current?.api?.getLastDisplayedRow(); ...bottomPlaceholderProps
if (lastRowIndex && rowCont - 1 === lastRowIndex) { } = useBottomPlaceholder<Order>({
const lastrow = gridRef,
gridRef.current?.api.getDisplayedRowAtIndex(lastRowIndex); disabled: !enforceBottomPlaceholder && !isReadOnly && !hasActiveOrder,
lastrow?.setRowHeight(50); });
makeBottomPlaceholders(lastrow?.data);
gridRef.current?.api.onRowHeightChanged();
gridRef.current?.api.refreshInfiniteCache();
}
}
}, [isReadOnly, hasActiveOrder, makeBottomPlaceholders]);
const onBodyScrollEnd = useCallback(
(event: BodyScrollEndEvent) => {
if (event.top === 0) {
addNewRows();
}
checkBottomPlaceholder();
},
[addNewRows, checkBottomPlaceholder]
);
const onBodyScroll = useCallback((event: BodyScrollEvent) => {
scrolledToTop.current = event.top <= 0;
}, []);
const onFilterChanged = useCallback( const onFilterChanged = useCallback(
(event: FilterChangedEvent) => { (event: FilterChangedEvent) => {
makeBottomPlaceholders();
const updatedFilter = event.api.getFilterModel(); const updatedFilter = event.api.getFilterModel();
if (Object.keys(updatedFilter).length) { if (Object.keys(updatedFilter).length) {
setFilter(updatedFilter); setFilter(updatedFilter);
} else { } else {
setFilter(undefined); setFilter(undefined);
} }
checkBottomPlaceholder(); setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0);
bottomPlaceholderOnFilterChanged?.();
}, },
[setFilter, makeBottomPlaceholders, checkBottomPlaceholder] [setFilter, bottomPlaceholderOnFilterChanged]
); );
const onSortChange = useCallback( const onSortChange = useCallback(
(event: SortChangedEvent) => { (event: SortChangedEvent) => {
makeBottomPlaceholders();
const sort = event.columnApi const sort = event.columnApi
.getColumnState() .getColumnState()
.sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0)) .sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0))
@ -148,9 +116,9 @@ export const OrderListManager = ({
return acc; return acc;
}, [] as { colId: string; sort: string }[]); }, [] as { colId: string; sort: string }[]);
setSort(sort.length > 0 ? sort : undefined); setSort(sort.length > 0 ? sort : undefined);
checkBottomPlaceholder(); bottomPlaceholderOnSortChanged?.();
}, },
[setSort, makeBottomPlaceholders, checkBottomPlaceholder] [setSort, bottomPlaceholderOnSortChanged]
); );
const cancel = useCallback( const cancel = useCallback(
@ -166,15 +134,13 @@ export const OrderListManager = ({
[create] [create]
); );
const onGridReady = useCallback( const onGridReady = useCallback(({ api }: GridReadyEvent) => {
({ api }: GridReadyEvent) => { api.setFilterModel(initialFilter);
api.setDatasource({ }, []);
getRows,
}); useEffect(() => {
api.setFilterModel(initialFilter); setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0);
}, }, [data]);
[getRows]
);
const cancelAll = useCallback( const cancelAll = useCallback(
(marketId?: string) => { (marketId?: string) => {
@ -186,20 +152,20 @@ export const OrderListManager = ({
}, },
[create] [create]
); );
const { isFullWidthRow, fullWidthCellRenderer, rowClassRules } = const extractedData =
useBottomPlaceholder<Order>({ data && !loading
gridRef, ? 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}
ref={gridRef} ref={gridRef}
rowModelType="infinite"
onGridReady={onGridReady} onGridReady={onGridReady}
onBodyScrollEnd={onBodyScrollEnd}
onBodyScroll={onBodyScroll}
onFilterChanged={onFilterChanged} onFilterChanged={onFilterChanged}
onSortChanged={onSortChange} onSortChanged={onSortChange}
cancel={cancel} cancel={cancel}
@ -209,9 +175,7 @@ export const OrderListManager = ({
blockLoadDebounceMillis={100} blockLoadDebounceMillis={100}
suppressLoadingOverlay suppressLoadingOverlay
suppressNoRowsOverlay suppressNoRowsOverlay
isFullWidthRow={isFullWidthRow} {...bottomPlaceholderProps}
fullWidthCellRenderer={fullWidthCellRenderer}
rowClassRules={rowClassRules}
/> />
<div className="pointer-events-none absolute inset-0"> <div className="pointer-events-none absolute inset-0">
<AsyncRenderer <AsyncRenderer
@ -219,7 +183,7 @@ export const OrderListManager = ({
error={error} error={error}
data={data} data={data}
noDataMessage={t('No orders')} noDataMessage={t('No orders')}
noDataCondition={(data) => !(data && data.length)} noDataCondition={(data) => !dataCount}
reload={reload} reload={reload}
/> />
</div> </div>

View File

@ -79,6 +79,9 @@ export const useOrderListData = ({
timeInForce: filter?.timeInForce?.value, timeInForce: filter?.timeInForce?.value,
types: filter?.type?.value, types: filter?.type?.value,
}, },
pagination: {
first: 1000,
},
}), }),
[partyId, marketId, filter] [partyId, marketId, filter]
); );
@ -98,7 +101,6 @@ export const useOrderListData = ({
({ ({
data, data,
delta, delta,
totalCount,
}: { }: {
data: (OrderEdge | null)[] | null; data: (OrderEdge | null)[] | null;
delta?: Order[]; delta?: Order[];
@ -112,7 +114,10 @@ export const useOrderListData = ({
).length; ).length;
} }
} }
return updateGridData(dataRef, data, gridRef); if (gridRef.current?.api?.getModel().getType() === 'infinite') {
return updateGridData(dataRef, data, gridRef);
}
return false;
}, },
[gridRef, scrolledToTop] [gridRef, scrolledToTop]
); );
@ -126,7 +131,10 @@ export const useOrderListData = ({
totalCount?: number; totalCount?: number;
}) => { }) => {
totalCountRef.current = totalCount; totalCountRef.current = totalCount;
return updateGridData(dataRef, data, gridRef); if (gridRef.current?.api?.getModel().getType() === 'infinite') {
return updateGridData(dataRef, data, gridRef);
}
return false;
}, },
[gridRef] [gridRef]
); );

View File

@ -48,7 +48,9 @@ describe('OrderListTable', () => {
await act(async () => { await act(async () => {
render(generateJsx({ rowData: [] })); render(generateJsx({ rowData: [] }));
}); });
expect(screen.getByText('No orders')).toBeInTheDocument(); expect(() => screen.getByText('No orders')).toThrow(
'Unable to find an element'
);
}); });
it('should render correct columns', async () => { it('should render correct columns', async () => {

View File

@ -39,10 +39,10 @@ export const OrderListTable = memo(
return ( return (
<AgGrid <AgGrid
ref={ref} ref={ref}
overlayNoRowsTemplate={t('No orders')}
defaultColDef={{ defaultColDef={{
flex: 1, flex: 1,
resizable: true, resizable: true,
sortable: true,
filterParams: { buttons: ['reset'] }, filterParams: { buttons: ['reset'] },
}} }}
style={{ style={{
@ -74,6 +74,7 @@ export const OrderListTable = memo(
value value
) )
} }
minWidth={150}
/> />
<AgGridColumn <AgGridColumn
headerName={t('Size')} headerName={t('Size')}
@ -89,7 +90,6 @@ export const OrderListTable = memo(
valueFormatter={({ valueFormatter={({
value, value,
data, data,
node,
}: VegaValueFormatterParams<Order, 'size'>) => { }: VegaValueFormatterParams<Order, 'size'>) => {
if (!data) { if (!data) {
return undefined; return undefined;
@ -110,6 +110,7 @@ export const OrderListTable = memo(
) )
); );
}} }}
minWidth={80}
/> />
<AgGridColumn <AgGridColumn
field="type" field="type"
@ -120,7 +121,6 @@ export const OrderListTable = memo(
valueFormatter={({ valueFormatter={({
data: order, data: order,
value, value,
node,
}: VegaValueFormatterParams<Order, 'type'>) => { }: VegaValueFormatterParams<Order, 'type'>) => {
if (!order) { if (!order) {
return undefined; return undefined;
@ -130,6 +130,7 @@ export const OrderListTable = memo(
if (order?.liquidityProvision) return t('Liquidity provision'); if (order?.liquidityProvision) return t('Liquidity provision');
return Schema.OrderTypeMapping[value]; return Schema.OrderTypeMapping[value];
}} }}
minWidth={80}
/> />
<AgGridColumn <AgGridColumn
field="status" field="status"
@ -160,6 +161,7 @@ export const OrderListTable = memo(
{valueFormatted} {valueFormatted}
</span> </span>
)} )}
minWidth={100}
/> />
<AgGridColumn <AgGridColumn
headerName={t('Filled')} headerName={t('Filled')}
@ -186,6 +188,7 @@ export const OrderListTable = memo(
dps dps
)}/${addDecimalsFormatNumber(size.toString(), dps)}`; )}/${addDecimalsFormatNumber(size.toString(), dps)}`;
}} }}
minWidth={100}
/> />
<AgGridColumn <AgGridColumn
field="price" field="price"
@ -208,6 +211,7 @@ export const OrderListTable = memo(
} }
return addDecimalsFormatNumber(value, data.market.decimalPlaces); return addDecimalsFormatNumber(value, data.market.decimalPlaces);
}} }}
minWidth={100}
/> />
<AgGridColumn <AgGridColumn
field="timeInForce" field="timeInForce"
@ -231,6 +235,7 @@ export const OrderListTable = memo(
return value ? Schema.OrderTimeInForceMapping[value] : ''; return value ? Schema.OrderTimeInForceMapping[value] : '';
}} }}
minWidth={150}
/> />
<AgGridColumn <AgGridColumn
field="createdAt" field="createdAt"
@ -244,6 +249,7 @@ export const OrderListTable = memo(
</span> </span>
); );
}} }}
minWidth={150}
/> />
<AgGridColumn <AgGridColumn
field="updatedAt" field="updatedAt"
@ -261,6 +267,7 @@ export const OrderListTable = memo(
</span> </span>
); );
}} }}
minWidth={150}
/> />
<AgGridColumn <AgGridColumn
colId="amend" colId="amend"
@ -284,6 +291,7 @@ export const OrderListTable = memo(
</> </>
) : null; ) : null;
}} }}
sortable={false}
/> />
</AgGrid> </AgGrid>
); );

View File

@ -68,7 +68,7 @@ export const useBottomPlaceholder = <T extends {}>({
isFullWidthRow, isFullWidthRow,
fullWidthCellRenderer, fullWidthCellRenderer,
onSortChanged: onRowsChanged, onSortChanged: onRowsChanged,
onFilterChange: onRowsChanged, onFilterChanged: onRowsChanged,
} }
: {}, : {},
[onBodyScrollEnd, onRowsChanged, disabled] [onBodyScrollEnd, onRowsChanged, disabled]

View File

@ -258,7 +258,16 @@ function makeDataProviderInternal<
client client
.query<QueryData>({ .query<QueryData>({
query, query,
variables: { ...variables, ...(pagination && { pagination }) }, variables: {
...variables,
...(pagination && {
// let the variables pagination be prior to provider param
pagination: {
...pagination,
...(variables?.['pagination'] ?? null),
},
}),
},
fetchPolicy: fetchPolicy || 'no-cache', fetchPolicy: fetchPolicy || 'no-cache',
context: additionalContext, context: additionalContext,
errorPolicy: policy || 'none', errorPolicy: policy || 'none',