[#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>(
|
||||
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
|
||||
update: (draft: Draft<Data>[] | null, delta: Delta) => void,
|
||||
update: (draft: Draft<Data>[], delta: Delta) => void,
|
||||
getData: (subscriptionData: QueryData) => Data[] | null,
|
||||
getDelta: (subscriptionData: SubscriptionData) => Delta
|
||||
) {
|
||||
@ -61,7 +61,7 @@ export function makeDataProvider<QueryData, Data, SubscriptionData, Delta>(
|
||||
return;
|
||||
}
|
||||
const delta = getDelta(subscriptionData);
|
||||
if (loading) {
|
||||
if (loading || !data) {
|
||||
updateQueue.push(delta);
|
||||
} else {
|
||||
const newData = produce(data, (draft) => {
|
||||
@ -77,7 +77,7 @@ export function makeDataProvider<QueryData, Data, SubscriptionData, Delta>(
|
||||
try {
|
||||
const res = await client.query<QueryData>({ query });
|
||||
data = getData(res.data);
|
||||
if (updateQueue && updateQueue.length > 0) {
|
||||
if (data && updateQueue && updateQueue.length > 0) {
|
||||
data = produce(data, (draft) => {
|
||||
while (updateQueue.length) {
|
||||
const delta = updateQueue.shift();
|
||||
|
@ -63,10 +63,7 @@ export const marketsDataProvider = makeDataProvider<
|
||||
>(
|
||||
MARKETS_QUERY,
|
||||
MARKET_DATA_SUB,
|
||||
(draft: Markets_markets[] | null, delta: MarketDataSub_marketData) => {
|
||||
if (!draft) {
|
||||
return;
|
||||
}
|
||||
(draft: Markets_markets[], delta: MarketDataSub_marketData) => {
|
||||
const index = draft.findIndex((m) => m.id === delta.market.id);
|
||||
if (index !== -1) {
|
||||
draft[index].data = delta;
|
||||
|
@ -77,13 +77,7 @@ export const positionsDataProvider = makeDataProvider<
|
||||
>(
|
||||
POSITION_QUERY,
|
||||
POSITIONS_SUB,
|
||||
(
|
||||
draft: positions_party_positions[] | null,
|
||||
delta: positionSubscribe_positions
|
||||
) => {
|
||||
if (!draft) {
|
||||
return;
|
||||
}
|
||||
(draft: positions_party_positions[], delta: positionSubscribe_positions) => {
|
||||
const index = draft.findIndex((m) => m.market.id === delta.market.id);
|
||||
if (index !== -1) {
|
||||
draft[index] = delta;
|
||||
|
@ -2,10 +2,10 @@ export * from './__generated__/AssetsQuery';
|
||||
export * from './__generated__/globalTypes';
|
||||
export * from './__generated__/Guess';
|
||||
export * from './__generated__/Market';
|
||||
export * from './__generated__/MarketDataFields';
|
||||
export * from './__generated__/MarketDataSub';
|
||||
export * from './__generated__/Markets';
|
||||
export * from './__generated__/MarketsQuery';
|
||||
export * from './__generated__/MarketDataSub';
|
||||
export * from './__generated__/MarketDataFields';
|
||||
export * from './__generated__/NetworkParametersQuery';
|
||||
export * from './__generated__/NodesQuery';
|
||||
export * from './__generated__/OrderEvent';
|
||||
@ -13,6 +13,9 @@ export * from './__generated__/OrderFields';
|
||||
export * from './__generated__/Orders';
|
||||
export * from './__generated__/OrderSub';
|
||||
export * from './__generated__/PartyAssetsQuery';
|
||||
export * from './__generated__/PositionDetails';
|
||||
export * from './__generated__/positions';
|
||||
export * from './__generated__/positionSubscribe';
|
||||
export * from './__generated__/ProposalsQuery';
|
||||
|
||||
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) =>
|
||||
[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(
|
||||
() =>
|
||||
new Intl.DateTimeFormat(getUserLocale(), {
|
||||
|
Loading…
Reference in New Issue
Block a user