chore(trading): add handle of autosizing selected columns - main branch (#4888)

This commit is contained in:
Maciek 2023-09-28 21:14:32 +02:00 committed by GitHub
parent 478cc9e753
commit ff2e2574f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 76 additions and 32 deletions

View File

@ -9,7 +9,7 @@ import {
testOrderAmendment, testOrderAmendment,
} from '../support/order-validation'; } from '../support/order-validation';
const orderSymbol = 'market.tradableInstrument.instrument.code'; const orderSymbol = 'instrument-code';
const orderSize = 'size'; const orderSize = 'size';
const orderType = 'type'; const orderType = 'type';
const orderStatus = 'status'; const orderStatus = 'status';
@ -229,10 +229,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
status: Schema.OrderStatus.STATUS_FILLED, status: Schema.OrderStatus.STATUS_FILLED,
}); });
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Filled'); cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Filled');
cy.get('[col-id="market.tradableInstrument.instrument.code"]').contains( cy.get(`[col-id="${orderSymbol}"]`).contains('[title="Future"]', 'Futr');
'[title="Future"]',
'Futr'
);
}); });
it('must see a rejected order', () => { it('must see a rejected order', () => {

View File

@ -28,14 +28,20 @@ export interface OrderContainerProps {
filter?: Filter; filter?: Filter;
} }
const AUTO_SIZE_COLUMNS = ['instrument-code'];
export const OrdersContainer = ({ filter }: OrderContainerProps) => { export const OrdersContainer = ({ filter }: OrderContainerProps) => {
const { pubKey, isReadOnly } = useVegaWallet(); const { pubKey, isReadOnly } = useVegaWallet();
const onMarketClick = useMarketClickHandler(true); const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler(); const onOrderTypeClick = useMarketLiquidityClickHandler();
const { gridState, updateGridState } = useOrderListGridState(filter); const { gridState, updateGridState } = useOrderListGridState(filter);
const gridStoreCallbacks = useDataGridEvents(gridState, (newState) => { const gridStoreCallbacks = useDataGridEvents(
updateGridState(filter, newState); gridState,
}); (newState) => {
updateGridState(filter, newState);
},
AUTO_SIZE_COLUMNS
);
if (!pubKey) { if (!pubKey) {
return <Splash>{t('Please connect Vega wallet')}</Splash>; return <Splash>{t('Please connect Vega wallet')}</Splash>;

View File

@ -10,6 +10,8 @@ import { create } from 'zustand';
import { persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
const AUTO_SIZE_COLUMNS = ['marketCode'];
export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => { export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => {
const onMarketClick = useMarketClickHandler(true); const onMarketClick = useMarketClickHandler(true);
const { pubKey, pubKeys, isReadOnly } = useVegaWallet(); const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
@ -17,7 +19,11 @@ export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => {
const showClosed = usePositionsStore((store) => store.showClosedMarkets); const showClosed = usePositionsStore((store) => store.showClosedMarkets);
const gridStore = usePositionsStore((store) => store.gridStore); const gridStore = usePositionsStore((store) => store.gridStore);
const updateGridStore = usePositionsStore((store) => store.updateGridStore); const updateGridStore = usePositionsStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, updateGridStore); const gridStoreCallbacks = useDataGridEvents(
gridStore,
updateGridStore,
AUTO_SIZE_COLUMNS
);
if (!pubKey) { if (!pubKey) {
return ( return (

View File

@ -5,8 +5,6 @@ import type { MutableRefObject } from 'react';
import { useRef } from 'react'; import { useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
const GRID_EVENT_DEBOUNCE_TIME = 500;
const gridProps = { const gridProps = {
rowData: [{ id: 1 }], rowData: [{ id: 1 }],
columnDefs: [ columnDefs: [
@ -18,25 +16,29 @@ const gridProps = {
], ],
style: { width: 500, height: 300 }, style: { width: 500, height: 300 },
}; };
const GRID_EVENT_DEBOUNCE_TIME = 300;
let gridRef: MutableRefObject<AgGridReact | null>;
function TestComponent({
hookParams,
}: {
hookParams: Parameters<typeof useDataGridEvents>;
}) {
const hookCallbacks = useDataGridEvents(...hookParams);
gridRef = useRef<AgGridReact | null>(null);
return <AgGridThemed gridRef={gridRef} {...gridProps} {...hookCallbacks} />;
}
// Not using render hook so I can pass event callbacks // Not using render hook so I can pass event callbacks
// to a rendered grid // to a rendered grid
function setup(...args: Parameters<typeof useDataGridEvents>) { function setup(...args: Parameters<typeof useDataGridEvents>) {
let gridRef; return render(<TestComponent hookParams={args} />);
function TestComponent() {
const hookCallbacks = useDataGridEvents(...args);
gridRef = useRef<AgGridReact | null>(null);
return <AgGridThemed gridRef={gridRef} {...gridProps} {...hookCallbacks} />;
}
render(<TestComponent />);
return gridRef as unknown as MutableRefObject<AgGridReact>;
} }
describe('useDataGridEvents', () => { describe('useDataGridEvents', () => {
const originalWarn = console.warn; const originalWarn = console.warn;
beforeAll(() => { beforeAll(() => {
gridRef = undefined;
jest.useFakeTimers(); jest.useFakeTimers();
// disabling some ag grid warnings that are caused by test setup only // disabling some ag grid warnings that are caused by test setup only
@ -55,15 +57,15 @@ describe('useDataGridEvents', () => {
columnState: undefined, columnState: undefined,
}; };
const result = setup(initialState, callback); setup(initialState, callback);
// column state was not updated, so the default width provided by the // column state was not updated, so the default width provided by the
// col def should be set // col def should be set
expect(result.current.columnApi.getColumnState()[0].width).toEqual( expect(gridRef.current?.columnApi.getColumnState()[0].width).toEqual(
gridProps.columnDefs[0].width gridProps.columnDefs[0].width
); );
// no filters set // no filters set
expect(result.current.api.getFilterModel()).toEqual({}); expect(gridRef.current?.api.getFilterModel()).toEqual({});
// Set filter // Set filter
const idFilter = { const idFilter = {
@ -72,7 +74,7 @@ describe('useDataGridEvents', () => {
type: 'equals', type: 'equals',
}; };
await act(async () => { await act(async () => {
result.current.api.setFilterModel({ gridRef.current?.api.setFilterModel({
id: idFilter, id: idFilter,
}); });
}); });
@ -88,7 +90,7 @@ describe('useDataGridEvents', () => {
}, },
}); });
callback.mockClear(); callback.mockClear();
expect(result.current.api.getFilterModel()['id']).toEqual(idFilter); expect(gridRef.current?.api.getFilterModel()['id']).toEqual(idFilter);
}); });
it('applies grid state on ready', async () => { it('applies grid state on ready', async () => {
@ -105,11 +107,11 @@ describe('useDataGridEvents', () => {
columnState: [colState], columnState: [colState],
}; };
const result = setup(initialState, jest.fn()); setup(initialState, jest.fn());
await waitFor(() => { await waitFor(() => {
expect(result.current.api.getFilterModel()['id']).toEqual(idFilter); expect(gridRef.current?.api.getFilterModel()['id']).toEqual(idFilter);
expect(result.current.columnApi.getColumnState()[0]).toEqual( expect(gridRef.current?.columnApi.getColumnState()[0]).toEqual(
expect.objectContaining(colState) expect.objectContaining(colState)
); );
}); });
@ -122,13 +124,13 @@ describe('useDataGridEvents', () => {
columnState: undefined, columnState: undefined,
}; };
const result = setup(initialState, callback); setup(initialState, callback);
const newWidth = 400; const newWidth = 400;
// Set col width multiple times // Set col width multiple times
await act(async () => { await act(async () => {
result.current.columnApi.setColumnWidth('id', newWidth); gridRef.current?.columnApi.setColumnWidth('id', newWidth);
}); });
expect(callback).not.toHaveBeenCalled(); expect(callback).not.toHaveBeenCalled();
@ -139,4 +141,23 @@ describe('useDataGridEvents', () => {
expect(callback).toHaveBeenCalledTimes(0); expect(callback).toHaveBeenCalledTimes(0);
}); });
it('columns for autosizing should be handle', () => {
const callback = jest.fn();
const initialState = {
filterModel: undefined,
columnState: undefined,
};
const { rerender } = setup(initialState, callback, ['id']);
jest.spyOn(gridRef.current?.columnApi, 'autoSizeColumns');
rerender(<TestComponent hookParams={[initialState, callback, ['id']]} />);
act(() => {
gridRef.current?.api.setRowData([{ id: 'test-id' }]);
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
});
expect(gridRef.current?.columnApi.autoSizeColumns).toHaveBeenCalledWith([
'id',
]);
});
}); });

View File

@ -6,6 +6,7 @@ import type {
FilterChangedEvent, FilterChangedEvent,
FirstDataRenderedEvent, FirstDataRenderedEvent,
SortChangedEvent, SortChangedEvent,
GridReadyEvent,
} from 'ag-grid-community'; } from 'ag-grid-community';
import { useCallback } from 'react'; import { useCallback } from 'react';
@ -17,7 +18,8 @@ type State = {
export const useDataGridEvents = ( export const useDataGridEvents = (
state: State, state: State,
callback: (data: State) => void callback: (data: State) => void,
autoSizeColumns?: string[]
) => { ) => {
/** /**
* Callback for filter events * Callback for filter events
@ -78,7 +80,7 @@ export const useDataGridEvents = (
* State only applied if found, otherwise columns sized to fit available space * State only applied if found, otherwise columns sized to fit available space
*/ */
const onGridReady = useCallback( const onGridReady = useCallback(
({ api, columnApi }: FirstDataRenderedEvent) => { ({ api, columnApi }: GridReadyEvent) => {
if (!api || !columnApi) return; if (!api || !columnApi) return;
if (state.columnState) { if (state.columnState) {
@ -97,6 +99,16 @@ export const useDataGridEvents = (
[state] [state]
); );
const onFirstDataRendered = useCallback(
({ columnApi }: FirstDataRenderedEvent) => {
if (!columnApi) return;
if (!state?.columnState && autoSizeColumns?.length) {
columnApi.autoSizeColumns(autoSizeColumns);
}
},
[state, autoSizeColumns]
);
return { return {
onGridReady, onGridReady,
// these events don't use the 'finished' flag // these events don't use the 'finished' flag
@ -106,5 +118,6 @@ export const useDataGridEvents = (
// these trigger a lot so this callback uses the 'finished' flag // these trigger a lot so this callback uses the 'finished' flag
onColumnMoved: onDebouncedColumnChange, onColumnMoved: onDebouncedColumnChange,
onColumnResized: onDebouncedColumnChange, onColumnResized: onDebouncedColumnChange,
onFirstDataRendered,
}; };
}; };

View File

@ -79,6 +79,7 @@ export const OrderListTable = memo<
() => [ () => [
{ {
headerName: t('Market'), headerName: t('Market'),
colId: 'instrument-code',
field: 'market.tradableInstrument.instrument.code', field: 'market.tradableInstrument.instrument.code',
cellRenderer: 'MarketNameCell', cellRenderer: 'MarketNameCell',
cellRendererParams: { idPath: 'market.id', onMarketClick }, cellRendererParams: { idPath: 'market.id', onMarketClick },