[#128] add <PositionsTable/> component
This commit is contained in:
parent
936ed3f9e7
commit
7f1632d44d
@ -10,7 +10,7 @@ import type { Subscription } from 'zen-observable-ts';
|
|||||||
export function makeDataProvider<QueryData, Data, SubscriptionData, Delta>(
|
export function makeDataProvider<QueryData, Data, SubscriptionData, Delta>(
|
||||||
query: DocumentNode | TypedDocumentNode<QueryData, any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
query: DocumentNode | TypedDocumentNode<QueryData, any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
subscriptionQuery: DocumentNode | TypedDocumentNode<SubscriptionData, any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
subscriptionQuery: DocumentNode | TypedDocumentNode<SubscriptionData, any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
update: (draft: Draft<Data>[] | null, delta: Delta) => void,
|
update: (draft: Draft<Data>[], delta: Delta) => void,
|
||||||
getData: (subscriptionData: QueryData) => Data[] | null,
|
getData: (subscriptionData: QueryData) => Data[] | null,
|
||||||
getDelta: (subscriptionData: SubscriptionData) => Delta
|
getDelta: (subscriptionData: SubscriptionData) => Delta
|
||||||
) {
|
) {
|
||||||
@ -61,7 +61,7 @@ export function makeDataProvider<QueryData, Data, SubscriptionData, Delta>(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const delta = getDelta(subscriptionData);
|
const delta = getDelta(subscriptionData);
|
||||||
if (loading) {
|
if (loading || !data) {
|
||||||
updateQueue.push(delta);
|
updateQueue.push(delta);
|
||||||
} else {
|
} else {
|
||||||
const newData = produce(data, (draft) => {
|
const newData = produce(data, (draft) => {
|
||||||
@ -77,7 +77,7 @@ export function makeDataProvider<QueryData, Data, SubscriptionData, Delta>(
|
|||||||
try {
|
try {
|
||||||
const res = await client.query<QueryData>({ query });
|
const res = await client.query<QueryData>({ query });
|
||||||
data = getData(res.data);
|
data = getData(res.data);
|
||||||
if (updateQueue && updateQueue.length > 0) {
|
if (data && updateQueue && updateQueue.length > 0) {
|
||||||
data = produce(data, (draft) => {
|
data = produce(data, (draft) => {
|
||||||
while (updateQueue.length) {
|
while (updateQueue.length) {
|
||||||
const delta = updateQueue.shift();
|
const delta = updateQueue.shift();
|
||||||
|
@ -63,10 +63,7 @@ export const marketsDataProvider = makeDataProvider<
|
|||||||
>(
|
>(
|
||||||
MARKETS_QUERY,
|
MARKETS_QUERY,
|
||||||
MARKET_DATA_SUB,
|
MARKET_DATA_SUB,
|
||||||
(draft: Markets_markets[] | null, delta: MarketDataSub_marketData) => {
|
(draft: Markets_markets[], delta: MarketDataSub_marketData) => {
|
||||||
if (!draft) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const index = draft.findIndex((m) => m.id === delta.market.id);
|
const index = draft.findIndex((m) => m.id === delta.market.id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
draft[index].data = delta;
|
draft[index].data = delta;
|
||||||
|
@ -77,13 +77,7 @@ export const positionsDataProvider = makeDataProvider<
|
|||||||
>(
|
>(
|
||||||
POSITION_QUERY,
|
POSITION_QUERY,
|
||||||
POSITIONS_SUB,
|
POSITIONS_SUB,
|
||||||
(
|
(draft: positions_party_positions[], delta: positionSubscribe_positions) => {
|
||||||
draft: positions_party_positions[] | null,
|
|
||||||
delta: positionSubscribe_positions
|
|
||||||
) => {
|
|
||||||
if (!draft) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const index = draft.findIndex((m) => m.market.id === delta.market.id);
|
const index = draft.findIndex((m) => m.market.id === delta.market.id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
draft[index] = delta;
|
draft[index] = delta;
|
||||||
|
@ -2,10 +2,10 @@ export * from './__generated__/AssetsQuery';
|
|||||||
export * from './__generated__/globalTypes';
|
export * from './__generated__/globalTypes';
|
||||||
export * from './__generated__/Guess';
|
export * from './__generated__/Guess';
|
||||||
export * from './__generated__/Market';
|
export * from './__generated__/Market';
|
||||||
|
export * from './__generated__/MarketDataFields';
|
||||||
|
export * from './__generated__/MarketDataSub';
|
||||||
export * from './__generated__/Markets';
|
export * from './__generated__/Markets';
|
||||||
export * from './__generated__/MarketsQuery';
|
export * from './__generated__/MarketsQuery';
|
||||||
export * from './__generated__/MarketDataSub';
|
|
||||||
export * from './__generated__/MarketDataFields';
|
|
||||||
export * from './__generated__/NetworkParametersQuery';
|
export * from './__generated__/NetworkParametersQuery';
|
||||||
export * from './__generated__/NodesQuery';
|
export * from './__generated__/NodesQuery';
|
||||||
export * from './__generated__/OrderEvent';
|
export * from './__generated__/OrderEvent';
|
||||||
@ -13,6 +13,9 @@ export * from './__generated__/OrderFields';
|
|||||||
export * from './__generated__/Orders';
|
export * from './__generated__/Orders';
|
||||||
export * from './__generated__/OrderSub';
|
export * from './__generated__/OrderSub';
|
||||||
export * from './__generated__/PartyAssetsQuery';
|
export * from './__generated__/PartyAssetsQuery';
|
||||||
|
export * from './__generated__/PositionDetails';
|
||||||
|
export * from './__generated__/positions';
|
||||||
|
export * from './__generated__/positionSubscribe';
|
||||||
export * from './__generated__/ProposalsQuery';
|
export * from './__generated__/ProposalsQuery';
|
||||||
|
|
||||||
export * from './data-providers';
|
export * from './data-providers';
|
||||||
|
@ -1 +1 @@
|
|||||||
export * from './lib/positions';
|
export * from './lib/positions-table';
|
||||||
|
22
libs/positions/src/lib/positions-table.spec.tsx
Normal file
22
libs/positions/src/lib/positions-table.spec.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { MockedProvider } from '@apollo/react-testing';
|
||||||
|
import PositionsTable from './positions-table';
|
||||||
|
|
||||||
|
describe('PositionsTable', () => {
|
||||||
|
it('should render successfully', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
const { baseElement } = render(
|
||||||
|
<MockedProvider>
|
||||||
|
<PositionsTable
|
||||||
|
data={[]}
|
||||||
|
onRowClicked={(marketId) => {
|
||||||
|
console.log(marketId);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
expect(baseElement).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
125
libs/positions/src/lib/positions-table.tsx
Normal file
125
libs/positions/src/lib/positions-table.tsx
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import { forwardRef, useMemo } from 'react';
|
||||||
|
import type { ValueFormatterParams } from 'ag-grid-community';
|
||||||
|
import {
|
||||||
|
PriceCell /*, MarginCell*/,
|
||||||
|
formatNumber,
|
||||||
|
volumePrefix,
|
||||||
|
addDecimal,
|
||||||
|
} from '@vegaprotocol/react-helpers';
|
||||||
|
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { AgGridColumn } from 'ag-grid-react';
|
||||||
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
import compact from 'lodash/compact';
|
||||||
|
import {
|
||||||
|
positions_party_positions,
|
||||||
|
MarketTradingMode,
|
||||||
|
} from '@vegaprotocol/graphql';
|
||||||
|
|
||||||
|
interface PositionsTableProps {
|
||||||
|
data: positions_party_positions[];
|
||||||
|
onRowClicked: (marketId: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getRowNodeId = (data: { market: { id: string } }) =>
|
||||||
|
data.market.id;
|
||||||
|
|
||||||
|
const sortByName = (
|
||||||
|
a: positions_party_positions,
|
||||||
|
b: positions_party_positions
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
a.market.tradableInstrument.instrument.name <
|
||||||
|
b.market.tradableInstrument.instrument.name
|
||||||
|
) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
a.market.tradableInstrument.instrument.name >
|
||||||
|
b.market.tradableInstrument.instrument.name
|
||||||
|
) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PositionsTable = forwardRef<AgGridReact, PositionsTableProps>(
|
||||||
|
({ data, onRowClicked }, ref) => {
|
||||||
|
const sortedData = useMemo(() => {
|
||||||
|
return compact(data).sort(sortByName);
|
||||||
|
}, [data]);
|
||||||
|
return (
|
||||||
|
<AgGrid
|
||||||
|
style={{ width: '100%', height: '100%' }}
|
||||||
|
overlayNoRowsTemplate="No positions"
|
||||||
|
rowData={sortedData}
|
||||||
|
getRowNodeId={getRowNodeId}
|
||||||
|
ref={ref}
|
||||||
|
defaultColDef={{
|
||||||
|
flex: 1,
|
||||||
|
resizable: true,
|
||||||
|
}}
|
||||||
|
onRowClicked={({ data }: { data: positions_party_positions }) =>
|
||||||
|
onRowClicked(getRowNodeId(data))
|
||||||
|
}
|
||||||
|
components={{ PriceCell }}
|
||||||
|
>
|
||||||
|
<AgGridColumn
|
||||||
|
headerName="Market"
|
||||||
|
field="tradableInstrument.instrument.code"
|
||||||
|
/>
|
||||||
|
<AgGridColumn
|
||||||
|
headerName="Amount"
|
||||||
|
field="openVolume"
|
||||||
|
valueFormatter={({ value, data }: ValueFormatterParams) =>
|
||||||
|
volumePrefix(value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<AgGridColumn
|
||||||
|
headerName="Average Entry Price"
|
||||||
|
field="averageEntryPrice"
|
||||||
|
cellRenderer="PriceCell"
|
||||||
|
valueFormatter={({ value, data }: ValueFormatterParams) =>
|
||||||
|
formatNumber(value, data.market.decimalPlaces)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<AgGridColumn
|
||||||
|
headerName="Mark Price"
|
||||||
|
field="market.data.markPrice"
|
||||||
|
type="rightAligned"
|
||||||
|
cellRenderer="PriceCell"
|
||||||
|
valueFormatter={({ value, data }: ValueFormatterParams) => {
|
||||||
|
if (
|
||||||
|
data.market.data.marketTradingMode ===
|
||||||
|
MarketTradingMode.OpeningAuction
|
||||||
|
) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
return addDecimal(value, data.market.decimalPlaces);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<AgGridColumn
|
||||||
|
headerName="Realised PNL"
|
||||||
|
field="realisedPNL"
|
||||||
|
type="rightAligned"
|
||||||
|
cellClassRules={{
|
||||||
|
'color-vega-green': ({ value }: { value: string }) =>
|
||||||
|
Number(value) > 0,
|
||||||
|
'color-vega-red': ({ value }: { value: string }) =>
|
||||||
|
Number(value) < 0,
|
||||||
|
}}
|
||||||
|
valueFormatter={({ value }: ValueFormatterParams) => {
|
||||||
|
if (Number(value) > 0) {
|
||||||
|
return '+' + value;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}}
|
||||||
|
cellRenderer="PriceCell"
|
||||||
|
/>
|
||||||
|
</AgGrid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default PositionsTable;
|
@ -1,10 +0,0 @@
|
|||||||
import { render } from '@testing-library/react';
|
|
||||||
|
|
||||||
import Positions from './positions';
|
|
||||||
|
|
||||||
describe('Positions', () => {
|
|
||||||
it('should render successfully', () => {
|
|
||||||
const { baseElement } = render(<Positions />);
|
|
||||||
expect(baseElement).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,14 +0,0 @@
|
|||||||
import './positions.module.scss';
|
|
||||||
|
|
||||||
/* eslint-disable-next-line */
|
|
||||||
export interface PositionsProps {}
|
|
||||||
|
|
||||||
export function Positions(props: PositionsProps) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>Welcome to Positions!</h1>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Positions;
|
|
@ -7,6 +7,19 @@ const getUserLocale = () => 'default';
|
|||||||
export const splitAt = (index: number) => (x: string) =>
|
export const splitAt = (index: number) => (x: string) =>
|
||||||
[x.slice(0, index), x.slice(index)];
|
[x.slice(0, index), x.slice(index)];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a number prefixed with either a '-' or a '+'. The open volume field
|
||||||
|
* already comes with a '-' if negative so we only need to actually prefix if
|
||||||
|
* its a positive value
|
||||||
|
*/
|
||||||
|
export function volumePrefix(value: string): string {
|
||||||
|
if (value === '0' || value.startsWith('-')) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '+' + value;
|
||||||
|
}
|
||||||
|
|
||||||
export const getTimeFormat = once(
|
export const getTimeFormat = once(
|
||||||
() =>
|
() =>
|
||||||
new Intl.DateTimeFormat(getUserLocale(), {
|
new Intl.DateTimeFormat(getUserLocale(), {
|
||||||
|
Loading…
Reference in New Issue
Block a user