feat(#2273): orders and fills on trading page to be market specific (#2395)

* feat(#2273): make orders and fills tabs market specific in trade grid

* feat(#2273): fix order navigation and show orders for this market only checkbox

* fix(#2273): fills container should not require market

* feat(#2273): add marketId as hook dependency

* fix: use data-testid in trading orders

* fix(#2273): default to false
This commit is contained in:
m.ray 2022-12-14 07:59:59 -05:00 committed by GitHub
parent b508d7b252
commit e49ad9da6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 182 additions and 147 deletions

View File

@ -141,10 +141,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
id: orderId,
status: Schema.OrderStatus.STATUS_ACTIVE,
});
cy.get(`[data-testid=order-status-${orderId}]`).should(
'have.text',
'Active'
);
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Active');
});
it('must see an expired order', () => {
// 7002-SORD-042
@ -152,10 +149,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
id: orderId,
status: Schema.OrderStatus.STATUS_EXPIRED,
});
cy.get(`[data-testid=order-status-${orderId}]`).should(
'have.text',
'Expired'
);
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Expired');
});
it('must see a cancelled order', () => {
@ -165,10 +159,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
id: orderId,
status: Schema.OrderStatus.STATUS_CANCELLED,
});
cy.get(`[data-testid=order-status-${orderId}]`).should(
'have.text',
'Cancelled'
);
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Cancelled');
});
it('must see a stopped order', () => {
@ -178,10 +169,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
id: orderId,
status: Schema.OrderStatus.STATUS_STOPPED,
});
cy.get(`[data-testid=order-status-${orderId}]`).should(
'have.text',
'Stopped'
);
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Stopped');
});
it('must see a partially filled order', () => {
@ -192,11 +180,11 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
size: '5',
remaining: '1',
});
cy.get(`[data-testid=order-status-${orderId}]`).should(
cy.getByTestId(`order-status-${orderId}`).should(
'have.text',
'PartiallyFilled'
);
cy.get(`[data-testid=order-status-${orderId}]`)
cy.getByTestId(`order-status-${orderId}`)
.parent()
.siblings(`[col-id=${orderRemaining}]`)
.should('have.text', '4/5');
@ -209,10 +197,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
id: orderId,
status: Schema.OrderStatus.STATUS_FILLED,
});
cy.get(`[data-testid=order-status-${orderId}]`).should(
'have.text',
'Filled'
);
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Filled');
});
it('must see a rejected order', () => {
@ -222,7 +207,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
status: Schema.OrderStatus.STATUS_REJECTED,
rejectionReason: Schema.OrderRejectionReason.ORDER_ERROR_INTERNAL_ERROR,
});
cy.get(`[data-testid=order-status-${orderId}]`).should(
cy.getByTestId(`order-status-${orderId}`).should(
'have.text',
'Rejected: Internal error'
);
@ -235,9 +220,6 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
id: orderId,
status: Schema.OrderStatus.STATUS_PARKED,
});
cy.get(`[data-testid=order-status-${orderId}]`).should(
'have.text',
'Parked'
);
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Parked');
});
});

View File

@ -71,100 +71,123 @@ const MainGrid = ({
}: {
marketId: string;
onSelect?: (marketId: string) => void;
}) => (
<ResizableGrid vertical>
<ResizableGridPanel minSize={75} priority={LayoutPriority.High}>
<ResizableGrid proportionalLayout={false} minSize={200}>
<ResizableGridPanel
priority={LayoutPriority.High}
minSize={200}
preferredSize="50%"
>
<TradeGridChild>
<Tabs>
<Tab id="candles" name={t('Candles')}>
<TradingViews.Candles marketId={marketId} />
</Tab>
<Tab id="depth" name={t('Depth')}>
<TradingViews.Depth marketId={marketId} />
</Tab>
<Tab id="liquidity" name={t('Liquidity')}>
<TradingViews.Liquidity marketId={marketId} />
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={330}
minSize={300}
>
<TradeGridChild>
<Tabs>
<Tab id="ticket" name={t('Ticket')}>
<TradingViews.Ticket marketId={marketId} />
</Tab>
<Tab id="info" name={t('Info')}>
<TradingViews.Info
marketId={marketId}
onSelect={(id: string) => {
onSelect?.(id);
}}
}) => {
const [showMarketOnly, setShowMarketOnly] = useState(false);
return (
<ResizableGrid vertical>
<ResizableGridPanel minSize={75} priority={LayoutPriority.High}>
<ResizableGrid proportionalLayout={false} minSize={200}>
<ResizableGridPanel
priority={LayoutPriority.High}
minSize={200}
preferredSize="50%"
>
<TradeGridChild>
<Tabs>
<Tab id="candles" name={t('Candles')}>
<TradingViews.Candles marketId={marketId} />
</Tab>
<Tab id="depth" name={t('Depth')}>
<TradingViews.Depth marketId={marketId} />
</Tab>
<Tab id="liquidity" name={t('Liquidity')}>
<TradingViews.Liquidity marketId={marketId} />
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={330}
minSize={300}
>
<TradeGridChild>
<Tabs>
<Tab id="ticket" name={t('Ticket')}>
<TradingViews.Ticket marketId={marketId} />
</Tab>
<Tab id="info" name={t('Info')}>
<TradingViews.Info
marketId={marketId}
onSelect={(id: string) => {
onSelect?.(id);
}}
/>
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={430}
minSize={200}
>
<TradeGridChild>
<Tabs>
<Tab id="orderbook" name={t('Orderbook')}>
<TradingViews.Orderbook marketId={marketId} />
</Tab>
<Tab id="trades" name={t('Trades')}>
<TradingViews.Trades marketId={marketId} />
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
</ResizableGrid>
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize="25%"
minSize={50}
>
<TradeGridChild>
<Tabs>
<Tab id="positions" name={t('Positions')}>
<VegaWalletContainer>
<TradingViews.Positions />
</VegaWalletContainer>
</Tab>
<Tab id="orders" name={t('Orders')}>
<VegaWalletContainer>
<label className="flex align-right whitespace-nowrap overflow-hidden text-ellipsis m-1 text-xs">
<input
className="mr-1"
type="checkbox"
checked={showMarketOnly}
onChange={() => setShowMarketOnly(!showMarketOnly)}
/>
{t('Show orders for this market only')}
</label>
<TradingViews.Orders
marketId={showMarketOnly ? marketId : ''}
/>
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={430}
minSize={200}
>
<TradeGridChild>
<Tabs>
<Tab id="orderbook" name={t('Orderbook')}>
<TradingViews.Orderbook marketId={marketId} />
</Tab>
<Tab id="trades" name={t('Trades')}>
<TradingViews.Trades marketId={marketId} />
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
</ResizableGrid>
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize="25%"
minSize={50}
>
<TradeGridChild>
<Tabs>
<Tab id="positions" name={t('Positions')}>
<VegaWalletContainer>
<TradingViews.Positions />
</VegaWalletContainer>
</Tab>
<Tab id="orders" name={t('Orders')}>
<VegaWalletContainer>
<TradingViews.Orders />
</VegaWalletContainer>
</Tab>
<Tab id="fills" name={t('Fills')}>
<VegaWalletContainer>
<TradingViews.Fills />
</VegaWalletContainer>
</Tab>
<Tab id="accounts" name={t('Collateral')}>
<VegaWalletContainer>
<TradingViews.Collateral />
</VegaWalletContainer>
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
</ResizableGrid>
);
</VegaWalletContainer>
</Tab>
<Tab id="fills" name={t('Fills')}>
<VegaWalletContainer>
<label className="flex align-right whitespace-nowrap overflow-hidden text-ellipsis m-1 text-xs">
<input
className="mr-1"
type="checkbox"
checked={showMarketOnly}
onChange={() => setShowMarketOnly(!showMarketOnly)}
/>
{t('Show fills for this market only')}
</label>
<TradingViews.Fills marketId={showMarketOnly ? marketId : ''} />
</VegaWalletContainer>
</Tab>
<Tab id="accounts" name={t('Collateral')}>
<VegaWalletContainer>
<TradingViews.Collateral />
</VegaWalletContainer>
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
</ResizableGrid>
);
};
const MainGridWrapped = memo(MainGrid);
export const TradeGrid = ({ market, onSelect }: TradeGridProps) => {

View File

@ -61,7 +61,9 @@ export const CandlesChartContainer = ({
<div className="h-full flex flex-col">
<div className="px-4 py-2 flex flex-row flex-wrap gap-4">
<DropdownMenu>
<DropdownMenuTrigger>{t('Interval')}</DropdownMenuTrigger>
<DropdownMenuTrigger>
{t(`Interval: ${intervalLabels[interval]}`)}
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuRadioGroup
value={interval}

View File

@ -3,7 +3,7 @@ import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { FillsManager } from './fills-manager';
export const FillsContainer = () => {
export const FillsContainer = ({ marketId }: { marketId?: string }) => {
const { pubKey } = useVegaWallet();
if (!pubKey) {
@ -14,5 +14,5 @@ export const FillsContainer = () => {
);
}
return <FillsManager partyId={pubKey} />;
return <FillsManager partyId={pubKey} marketId={marketId} />;
};

View File

@ -7,13 +7,15 @@ import { useFillsList } from './use-fills-list';
interface FillsManagerProps {
partyId: string;
marketId?: string;
}
export const FillsManager = ({ partyId }: FillsManagerProps) => {
export const FillsManager = ({ partyId, marketId }: FillsManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const scrolledToTop = useRef(true);
const { data, error, loading, addNewRows, getRows } = useFillsList({
partyId,
marketId,
gridRef,
scrolledToTop,
});

View File

@ -10,11 +10,17 @@ import { fillsWithMarketProvider } from './fills-data-provider';
interface Props {
partyId: string;
marketId?: string;
gridRef: RefObject<AgGridReact>;
scrolledToTop: RefObject<boolean>;
}
export const useFillsList = ({ partyId, gridRef, scrolledToTop }: Props) => {
export const useFillsList = ({
partyId,
marketId,
gridRef,
scrolledToTop,
}: Props) => {
const dataRef = useRef<(TradeEdge | null)[] | null>(null);
const totalCountRef = useRef<number | undefined>(undefined);
const newRows = useRef(0);
@ -72,7 +78,7 @@ export const useFillsList = ({ partyId, gridRef, scrolledToTop }: Props) => {
[]
);
const variables = useMemo(() => ({ partyId }), [partyId]);
const variables = useMemo(() => ({ partyId, marketId }), [partyId, marketId]);
const { data, error, loading, load, totalCount } = useDataProvider<
(TradeEdge | null)[],

View File

@ -67,6 +67,7 @@ export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
tooltipComponent: TooltipCellComponent,
sortable: true,
}}
enableCellTextSelection={true}
rowData={data}
>
<AgGridColumn

View File

@ -62,8 +62,8 @@ fragment OrderUpdateFields on OrderUpdate {
}
}
subscription OrdersUpdate($partyId: ID!) {
orders(partyId: $partyId) {
subscription OrdersUpdate($partyId: ID!, $marketId: ID) {
orders(partyId: $partyId, marketId: $marketId) {
...OrderUpdateFields
}
}

View File

@ -18,6 +18,7 @@ export type OrderUpdateFieldsFragment = { __typename?: 'OrderUpdate', id: string
export type OrdersUpdateSubscriptionVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
marketId?: Types.InputMaybe<Types.Scalars['ID']>;
}>;
@ -121,8 +122,8 @@ export type OrdersQueryHookResult = ReturnType<typeof useOrdersQuery>;
export type OrdersLazyQueryHookResult = ReturnType<typeof useOrdersLazyQuery>;
export type OrdersQueryResult = Apollo.QueryResult<OrdersQuery, OrdersQueryVariables>;
export const OrdersUpdateDocument = gql`
subscription OrdersUpdate($partyId: ID!) {
orders(partyId: $partyId) {
subscription OrdersUpdate($partyId: ID!, $marketId: ID) {
orders(partyId: $partyId, marketId: $marketId) {
...OrderUpdateFields
}
}
@ -141,6 +142,7 @@ export const OrdersUpdateDocument = gql`
* const { data, loading, error } = useOrdersUpdateSubscription({
* variables: {
* partyId: // value for 'partyId'
* marketId: // value for 'marketId'
* },
* });
*/
@ -149,4 +151,4 @@ export function useOrdersUpdateSubscription(baseOptions: Apollo.SubscriptionHook
return Apollo.useSubscription<OrdersUpdateSubscription, OrdersUpdateSubscriptionVariables>(OrdersUpdateDocument, options);
}
export type OrdersUpdateSubscriptionHookResult = ReturnType<typeof useOrdersUpdateSubscription>;
export type OrdersUpdateSubscriptionResult = Apollo.SubscriptionResult<OrdersUpdateSubscription>;
export type OrdersUpdateSubscriptionResult = Apollo.SubscriptionResult<OrdersUpdateSubscription>;

View File

@ -3,12 +3,12 @@ import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { OrderListManager } from './order-list-manager';
export const OrderListContainer = () => {
export const OrderListContainer = ({ marketId }: { marketId?: string }) => {
const { pubKey } = useVegaWallet();
if (!pubKey) {
return <Splash>{t('Please connect Vega wallet')}</Splash>;
}
return <OrderListManager partyId={pubKey} />;
return <OrderListManager partyId={pubKey} marketId={marketId} />;
};

View File

@ -15,9 +15,13 @@ import type { Filter, Sort } from './use-order-list-data';
export interface OrderListManagerProps {
partyId: string;
marketId?: string;
}
export const OrderListManager = ({ partyId }: OrderListManagerProps) => {
export const OrderListManager = ({
partyId,
marketId,
}: OrderListManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const scrolledToTop = useRef(true);
const [sort, setSort] = useState<Sort[] | undefined>();
@ -25,6 +29,7 @@ export const OrderListManager = ({ partyId }: OrderListManagerProps) => {
const { data, error, loading, addNewRows, getRows } = useOrderListData({
partyId,
marketId,
sort,
filter,
gridRef,
@ -74,6 +79,7 @@ export const OrderListManager = ({ partyId }: OrderListManagerProps) => {
onBodyScroll={onBodyScroll}
onFilterChanged={onFilterChanged}
onSortChanged={onSortChange}
marketId={marketId}
/>
<div className="pointer-events-none absolute inset-0 top-5">
<AsyncRenderer

View File

@ -10,7 +10,10 @@ import type {
OrderEdge,
Order,
} from '../order-data-provider/order-data-provider';
import type { OrdersQueryVariables } from '../order-data-provider/__generated__/Orders';
import type {
OrdersQueryVariables,
OrdersUpdateSubscriptionVariables,
} from '../order-data-provider/__generated__/Orders';
import type * as Types from '@vegaprotocol/types';
export interface Sort {
colId: string;
@ -32,6 +35,7 @@ export interface Filter {
}
interface Props {
partyId: string;
marketId?: string;
filter?: Filter;
sort?: Sort[];
gridRef: RefObject<AgGridReact>;
@ -40,6 +44,7 @@ interface Props {
export const useOrderListData = ({
partyId,
marketId,
sort,
filter,
gridRef,
@ -49,9 +54,11 @@ export const useOrderListData = ({
const totalCountRef = useRef<number | undefined>(undefined);
const newRows = useRef(0);
const variables = useMemo<OrdersQueryVariables>(
() => ({ partyId, dateRange: filter?.updatedAt?.value }),
[partyId, filter]
const variables = useMemo<
OrdersQueryVariables & OrdersUpdateSubscriptionVariables
>(
() => ({ partyId, dateRange: filter?.updatedAt?.value, marketId }),
[partyId, marketId, filter]
);
const addNewRows = useCallback(() => {
@ -85,11 +92,14 @@ export const useOrderListData = ({
(dataRef.current?.length && data?.length) ||
(!dataRef.current?.length && !data?.length)
);
dataRef.current = data;
dataRef.current =
!!marketId && !!data
? data.filter((d) => d?.node.market?.id === marketId)
: data;
gridRef.current?.api?.refreshInfiniteCache();
return avoidRerender;
},
[gridRef, scrolledToTop]
[gridRef, scrolledToTop, marketId]
);
const insert = useCallback(
@ -100,11 +110,14 @@ export const useOrderListData = ({
data: (OrderEdge | null)[] | null;
totalCount?: number;
}) => {
dataRef.current = data;
dataRef.current =
!!marketId && !!data
? data.filter((d) => d?.node.market?.id === marketId)
: data;
totalCountRef.current = totalCount;
return true;
},
[]
[marketId]
);
const { data, error, loading, load, totalCount } = useDataProvider({

View File

@ -36,7 +36,7 @@ import type { AgGridReact } from 'ag-grid-react';
import type { Order } from '../order-data-provider';
import type { OrderEventFieldsFragment } from '../../order-hooks';
type OrderListProps = TypedDataAgGrid<Order>;
type OrderListProps = TypedDataAgGrid<Order> & { marketId?: string };
export const TransactionComplete = ({
transaction,
@ -84,7 +84,7 @@ export const OrderList = forwardRef<AgGridReact, OrderListProps>(
<OrderListTable
{...props}
cancelAll={() => {
orderCancel.cancel({});
orderCancel.cancel({ marketId: props.marketId });
}}
cancel={(order: Order) => {
if (!order.market) return;
@ -174,9 +174,7 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
'market.tradableInstrument.instrument.code'
>) =>
data?.market?.id ? (
<Link href={`/markets/${data?.market?.id}`} target="_blank">
{value}
</Link>
<Link href={`/#/markets/${data?.market?.id}`}>{value}</Link>
) : (
value
)