diff --git a/libs/ledger/jest.config.ts b/libs/ledger/jest.config.ts index deafd8379..3c3e0b5cd 100644 --- a/libs/ledger/jest.config.ts +++ b/libs/ledger/jest.config.ts @@ -12,4 +12,5 @@ export default { }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], coverageDirectory: '../../coverage/libs/ledger', + setupFilesAfterEnv: ['./src/setup-tests.ts'], }; diff --git a/libs/ledger/src/lib/ledger-entries-data-provider.ts b/libs/ledger/src/lib/ledger-entries-data-provider.ts index 0f8a31f69..893214335 100644 --- a/libs/ledger/src/lib/ledger-entries-data-provider.ts +++ b/libs/ledger/src/lib/ledger-entries-data-provider.ts @@ -30,7 +30,10 @@ export type LedgerEntry = LedgerEntryFragment & { }; export type AggregatedLedgerEntriesEdge = Schema.AggregatedLedgerEntriesEdge; -export type AggregatedLedgerEntriesNode = AggregatedLedgerEntriesEdge & { +export type AggregatedLedgerEntriesNode = Omit< + AggregatedLedgerEntriesEdge, + 'node' +> & { node: LedgerEntry; }; diff --git a/libs/ledger/src/lib/ledger-entries.mock.ts b/libs/ledger/src/lib/ledger-entries.mock.ts index 8e31a31e9..1033a8c0c 100644 --- a/libs/ledger/src/lib/ledger-entries.mock.ts +++ b/libs/ledger/src/lib/ledger-entries.mock.ts @@ -32,7 +32,7 @@ export const ledgerEntriesQuery = ( return merge(defaultResult, override); }; -const ledgerEntries: LedgerEntryFragment[] = [ +export const ledgerEntries: LedgerEntryFragment[] = [ { vegaTime: '1669224476734364000', quantity: '0', @@ -180,7 +180,6 @@ const ledgerEntries: LedgerEntryFragment[] = [ toAccountBalance: '0', fromAccountBalance: '0', }, - { vegaTime: '2022-11-24T12:41:22.054428Z', quantity: '1000000000', diff --git a/libs/ledger/src/lib/ledger-export-link.spec.tsx b/libs/ledger/src/lib/ledger-export-link.spec.tsx new file mode 100644 index 000000000..8c623ccfa --- /dev/null +++ b/libs/ledger/src/lib/ledger-export-link.spec.tsx @@ -0,0 +1,85 @@ +import { act, render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { LedgerExportLink } from './ledger-export-link'; +import * as Types from '@vegaprotocol/types'; +import { ledgerEntries } from './ledger-entries.mock'; +import type { LedgerEntry } from './ledger-entries-data-provider'; + +const VEGA_URL = 'https://vega-url.co.uk/querystuff'; +const mockEnvironment = jest.fn(() => VEGA_URL); +jest.mock('@vegaprotocol/environment', () => ({ + useEnvironment: jest.fn(() => mockEnvironment()), +})); + +const asset = { + id: 'assetID', + name: 'assetName', + symbol: 'assetSymbol', + decimals: 1, + quantum: '1', + status: Types.AssetStatus, + source: { + __typename: 'ERC20', + contractAddress: 'contractAddres', + lifetimeLimit: 'lifetimeLimit', + withdrawThreshold: 'withdraw', + }, +}; + +describe('LedgerExportLink', () => { + const partyId = 'partyId'; + const entries = ledgerEntries.map((entry) => { + return { + ...entry, + asset: entry.assetId + ? { + ...asset, + id: entry.assetId, + name: `name ${entry.assetId}`, + symbol: `symbol ${entry.assetId}`, + } + : null, + marketSender: null, + marketReceiver: null, + } as LedgerEntry; + }); + + it('should be properly rendered', async () => { + render(); + await waitFor(() => { + expect(screen.getByRole('link')).toBeInTheDocument(); + expect(screen.getByRole('link')).toHaveAttribute( + 'href', + `https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=asset-id` + ); + expect( + screen.getByRole('button', { name: /^symbol asset-id/ }) + ).toBeInTheDocument(); + }); + }); + it('should be properly change link url', async () => { + render(); + await waitFor(() => { + expect( + screen.getByRole('button', { name: /^symbol asset-id/ }) + ).toBeInTheDocument(); + }); + act(() => { + userEvent.click(screen.getByRole('button', { name: /^symbol asset-id/ })); + }); + await waitFor(() => { + expect(screen.getByRole('menu')).toBeInTheDocument(); + }); + act(() => { + userEvent.click( + screen.getByRole('menuitem', { name: /^symbol asset-id-2/ }) + ); + }); + await waitFor(() => { + expect(screen.getByRole('link')).toHaveAttribute( + 'href', + `https://vega-url.co.uk/api/v2/ledgerentry/export?partyId=${partyId}&assetId=asset-id-2` + ); + }); + }); +}); diff --git a/libs/ledger/src/lib/ledger-export-link.tsx b/libs/ledger/src/lib/ledger-export-link.tsx new file mode 100644 index 000000000..9de96bbc9 --- /dev/null +++ b/libs/ledger/src/lib/ledger-export-link.tsx @@ -0,0 +1,71 @@ +import type { LedgerEntry } from './ledger-entries-data-provider'; +import { useMemo, useState } from 'react'; +import { useEnvironment } from '@vegaprotocol/environment'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + Link, + Button, +} from '@vegaprotocol/ui-toolkit'; +import { t } from '@vegaprotocol/i18n'; + +const getProtoHost = (vegaurl: string) => { + const loc = new URL(vegaurl); + return `${loc.protocol}//${loc.host}`; +}; + +export const LedgerExportLink = ({ + partyId, + entries, +}: { + partyId: string; + entries: LedgerEntry[]; +}) => { + const assets = entries.reduce((aggr, item) => { + if (item.asset && !(item.asset.id in aggr)) { + aggr[item.asset.id] = item.asset.symbol; + } + return aggr; + }, {} as Record); + const [assetId, setAssetId] = useState(Object.keys(assets)[0]); + const VEGA_URL = useEnvironment((store) => store.VEGA_URL); + const protohost = VEGA_URL ? getProtoHost(VEGA_URL) : ''; + + const assetDropDown = useMemo(() => { + return ( + {assets[assetId]}} + > + + {Object.keys(assets).map((assetKey) => ( + setAssetId(assetKey)} + > + {assets[assetKey]} + + ))} + + + ); + }, [assetId, assets]); + + if (!protohost) { + return null; + } + return ( +
+
Export all
+ {assetDropDown} + + + +
+ ); +}; diff --git a/libs/ledger/src/lib/ledger-manager.tsx b/libs/ledger/src/lib/ledger-manager.tsx index a6771d2e1..a4ca28c2f 100644 --- a/libs/ledger/src/lib/ledger-manager.tsx +++ b/libs/ledger/src/lib/ledger-manager.tsx @@ -3,12 +3,13 @@ import type * as Schema from '@vegaprotocol/types'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import type { FilterChangedEvent } from 'ag-grid-community'; import type { AgGridReact } from 'ag-grid-react'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { subDays, formatRFC3339 } from 'date-fns'; import type { AggregatedLedgerEntriesNode } from './ledger-entries-data-provider'; import { useLedgerEntriesDataProvider } from './ledger-entries-data-provider'; import { LedgerTable } from './ledger-table'; import type * as Types from '@vegaprotocol/types'; +import { LedgerExportLink } from './ledger-export-link'; export interface Filter { vegaTime?: { @@ -22,6 +23,7 @@ const defaultFilter = { value: { start: formatRFC3339(subDays(Date.now(), 7)) }, }, }; + export const LedgerManager = ({ partyId }: { partyId: string }) => { const gridRef = useRef(null); const [filter, setFilter] = useState(defaultFilter); @@ -55,6 +57,9 @@ export const LedgerManager = ({ partyId }: { partyId: string }) => { rowData={extractedData} onFilterChanged={onFilterChanged} /> + {extractedData && ( + + )}
( (props, ref) => { return (