feat(trading): view stop order history (#4586)
This commit is contained in:
parent
0767139712
commit
d3dbdd2bd5
@ -128,6 +128,9 @@ fragment StopOrderFields on StopOrder {
|
||||
updatedAt
|
||||
partyId
|
||||
marketId
|
||||
order {
|
||||
...OrderFields
|
||||
}
|
||||
trigger {
|
||||
... on StopOrderPrice {
|
||||
price
|
||||
|
@ -34,22 +34,51 @@ export type OrdersUpdateSubscription = { __typename?: 'Subscription', orders?: A
|
||||
|
||||
export type OrderSubmissionFieldsFragment = { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null };
|
||||
|
||||
export type StopOrderFieldsFragment = { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } };
|
||||
export type StopOrderFieldsFragment = { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, order?: { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null } | null, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } };
|
||||
|
||||
export type StopOrdersQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type StopOrdersQuery = { __typename?: 'Query', stopOrders?: { __typename?: 'StopOrderConnection', edges?: Array<{ __typename?: 'StopOrderEdge', node?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null }> | null } | null };
|
||||
export type StopOrdersQuery = { __typename?: 'Query', stopOrders?: { __typename?: 'StopOrderConnection', edges?: Array<{ __typename?: 'StopOrderEdge', node?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, order?: { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null } | null, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null }> | null } | null };
|
||||
|
||||
export type StopOrderByIdQueryVariables = Types.Exact<{
|
||||
stopOrderId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type StopOrderByIdQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null };
|
||||
export type StopOrderByIdQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, order?: { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null } | null, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null };
|
||||
|
||||
export const OrderUpdateFieldsFragmentDoc = gql`
|
||||
fragment OrderUpdateFields on OrderUpdate {
|
||||
id
|
||||
marketId
|
||||
type
|
||||
side
|
||||
size
|
||||
status
|
||||
rejectionReason
|
||||
price
|
||||
timeInForce
|
||||
remaining
|
||||
expiresAt
|
||||
createdAt
|
||||
updatedAt
|
||||
liquidityProvisionId
|
||||
peggedOrder {
|
||||
__typename
|
||||
reference
|
||||
offset
|
||||
}
|
||||
icebergOrder {
|
||||
__typename
|
||||
peakSize
|
||||
minimumVisibleSize
|
||||
reservedRemaining
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const OrderFieldsFragmentDoc = gql`
|
||||
fragment OrderFields on Order {
|
||||
id
|
||||
@ -85,35 +114,6 @@ export const OrderFieldsFragmentDoc = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const OrderUpdateFieldsFragmentDoc = gql`
|
||||
fragment OrderUpdateFields on OrderUpdate {
|
||||
id
|
||||
marketId
|
||||
type
|
||||
side
|
||||
size
|
||||
status
|
||||
rejectionReason
|
||||
price
|
||||
timeInForce
|
||||
remaining
|
||||
expiresAt
|
||||
createdAt
|
||||
updatedAt
|
||||
liquidityProvisionId
|
||||
peggedOrder {
|
||||
__typename
|
||||
reference
|
||||
offset
|
||||
}
|
||||
icebergOrder {
|
||||
__typename
|
||||
peakSize
|
||||
minimumVisibleSize
|
||||
reservedRemaining
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const OrderSubmissionFieldsFragmentDoc = gql`
|
||||
fragment OrderSubmissionFields on OrderSubmission {
|
||||
marketId
|
||||
@ -144,6 +144,9 @@ export const StopOrderFieldsFragmentDoc = gql`
|
||||
updatedAt
|
||||
partyId
|
||||
marketId
|
||||
order {
|
||||
...OrderFields
|
||||
}
|
||||
trigger {
|
||||
... on StopOrderPrice {
|
||||
price
|
||||
@ -156,7 +159,8 @@ export const StopOrderFieldsFragmentDoc = gql`
|
||||
...OrderSubmissionFields
|
||||
}
|
||||
}
|
||||
${OrderSubmissionFieldsFragmentDoc}`;
|
||||
${OrderFieldsFragmentDoc}
|
||||
${OrderSubmissionFieldsFragmentDoc}`;
|
||||
export const OrderByIdDocument = gql`
|
||||
query OrderById($orderId: ID!) {
|
||||
orderByID(id: $orderId) {
|
||||
|
@ -296,7 +296,7 @@ export const OrderListTable = memo<
|
||||
</ButtonLink>
|
||||
</>
|
||||
)}
|
||||
<ActionsDropdown data-testid="market-actions-content">
|
||||
<ActionsDropdown data-testid="order-actions-content">
|
||||
<TradingDropdownCopyItem
|
||||
value={data.id}
|
||||
text={t('Copy order ID')}
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { StopOrdersTable } from '../stop-orders-table/stop-orders-table';
|
||||
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import { useVegaTransactionStore } from '@vegaprotocol/wallet';
|
||||
import type { StopOrder } from '../order-data-provider/stop-orders-data-provider';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { stopOrdersWithMarketProvider } from '../order-data-provider/stop-orders-data-provider';
|
||||
import { OrderViewDialog } from '../order-list/order-view-dialog';
|
||||
import type { Order } from '../order-data-provider';
|
||||
|
||||
export interface StopOrdersManagerProps {
|
||||
partyId: string;
|
||||
@ -23,6 +25,7 @@ export const StopOrdersManager = ({
|
||||
gridProps,
|
||||
}: StopOrdersManagerProps) => {
|
||||
const create = useVegaTransactionStore((state) => state.create);
|
||||
const [viewOrder, setViewOrder] = useState<Order | null>(null);
|
||||
const variables = { partyId };
|
||||
|
||||
const { data, error, reload } = useDataProvider({
|
||||
@ -53,14 +56,25 @@ export const StopOrdersManager = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<StopOrdersTable
|
||||
rowData={data}
|
||||
onCancel={cancel}
|
||||
onMarketClick={onMarketClick}
|
||||
isReadOnly={isReadOnly}
|
||||
suppressAutoSize
|
||||
overlayNoRowsTemplate={error ? error.message : t('No stop orders')}
|
||||
{...gridProps}
|
||||
/>
|
||||
<>
|
||||
<StopOrdersTable
|
||||
rowData={data}
|
||||
onCancel={cancel}
|
||||
onView={setViewOrder}
|
||||
onMarketClick={onMarketClick}
|
||||
isReadOnly={isReadOnly}
|
||||
suppressAutoSize
|
||||
overlayNoRowsTemplate={error ? error.message : t('No stop orders')}
|
||||
{...gridProps}
|
||||
/>
|
||||
{viewOrder && (
|
||||
<OrderViewDialog
|
||||
isOpen={Boolean(viewOrder)}
|
||||
order={viewOrder}
|
||||
onChange={() => setViewOrder(null)}
|
||||
onMarketClick={onMarketClick}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ import type { PartialDeep } from 'type-fest';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import {
|
||||
StopOrdersTable,
|
||||
type StopOrdersTableProps,
|
||||
@ -27,6 +28,7 @@ jest.mock('@vegaprotocol/utils', () => ({
|
||||
}));
|
||||
|
||||
const defaultProps: StopOrdersTableProps = {
|
||||
onView: jest.fn(),
|
||||
rowData: [],
|
||||
onCancel: jest.fn(),
|
||||
isReadOnly: false,
|
||||
@ -104,6 +106,7 @@ const rowData = [
|
||||
generateStopOrder({
|
||||
id: 'stop-order-6',
|
||||
status: Schema.StopOrderStatus.STATUS_TRIGGERED,
|
||||
order: { id: 'order-id' },
|
||||
}),
|
||||
];
|
||||
|
||||
@ -234,4 +237,37 @@ describe('StopOrdersTable', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('shows actions dropdown only for triggered stop orders', async () => {
|
||||
await act(async () => {
|
||||
render(generateJsx({ rowData }));
|
||||
});
|
||||
const dropdownMenuButtons = screen.getAllByTestId('dropdown-menu');
|
||||
expect(dropdownMenuButtons).toHaveLength(1);
|
||||
dropdownMenuButtons.forEach((dropdownMenuButton) => {
|
||||
const id = dropdownMenuButton
|
||||
.closest('[role="row"]')
|
||||
?.getAttribute('row-id');
|
||||
expect(rowData.find((row) => row.id === id)?.status).toEqual(
|
||||
Schema.StopOrderStatus.STATUS_TRIGGERED
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('action dropdown has copy and view order actions', async () => {
|
||||
const onView = jest.fn();
|
||||
const user = userEvent.setup();
|
||||
await act(async () => {
|
||||
render(generateJsx({ rowData, onView }));
|
||||
});
|
||||
const dropdownMenuButtons = screen.getByTestId('dropdown-menu');
|
||||
dropdownMenuButtons.click();
|
||||
await user.click(dropdownMenuButtons as HTMLButtonElement);
|
||||
const menuItems = screen.getAllByRole('menuitem');
|
||||
expect(menuItems).toHaveLength(2);
|
||||
expect(menuItems[0]).toHaveTextContent('Copy order ID');
|
||||
expect(menuItems[1]).toHaveTextContent('View order details');
|
||||
menuItems[1].click();
|
||||
expect(onView).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
@ -7,7 +7,14 @@ import {
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
ActionsDropdown,
|
||||
ButtonLink,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
DropdownMenuItem,
|
||||
TradingDropdownCopyItem,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { ForwardedRef } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import {
|
||||
@ -28,6 +35,7 @@ import type {
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { StopOrder } from '../order-data-provider/stop-orders-data-provider';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
import type { Order } from '../order-data-provider';
|
||||
|
||||
const defaultColDef = {
|
||||
resizable: true,
|
||||
@ -38,12 +46,13 @@ const defaultColDef = {
|
||||
export type StopOrdersTableProps = TypedDataAgGrid<StopOrder> & {
|
||||
onCancel: (order: StopOrder) => void;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
onView: (order: Order) => void;
|
||||
isReadOnly: boolean;
|
||||
};
|
||||
|
||||
export const StopOrdersTable = memo<
|
||||
StopOrdersTableProps & { ref?: ForwardedRef<AgGridReact> }
|
||||
>(({ onCancel, onMarketClick, ...props }: StopOrdersTableProps) => {
|
||||
>(({ onCancel, onView, onMarketClick, ...props }: StopOrdersTableProps) => {
|
||||
const showAllActions = !props.isReadOnly;
|
||||
const columnDefs: ColDef[] = useMemo(
|
||||
() => [
|
||||
@ -236,12 +245,32 @@ export const StopOrdersTable = memo<
|
||||
{t('Cancel')}
|
||||
</ButtonLink>
|
||||
)}
|
||||
{data.status === Schema.StopOrderStatus.STATUS_TRIGGERED &&
|
||||
data.order && (
|
||||
<ActionsDropdown data-testid="stop-order-actions-content">
|
||||
<TradingDropdownCopyItem
|
||||
value={data.order.id}
|
||||
text={t('Copy order ID')}
|
||||
/>
|
||||
<DropdownMenuItem
|
||||
key={'view-order'}
|
||||
data-testid="view-order"
|
||||
onClick={() =>
|
||||
data.order &&
|
||||
onView({ ...data.order, market: data.market })
|
||||
}
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.INFO} size={16} />
|
||||
{t('View order details')}
|
||||
</DropdownMenuItem>
|
||||
</ActionsDropdown>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
[onCancel, onMarketClick, props.isReadOnly, showAllActions]
|
||||
[onCancel, onMarketClick, onView, props.isReadOnly, showAllActions]
|
||||
);
|
||||
|
||||
return (
|
||||
|
Loading…
Reference in New Issue
Block a user