Merge branch 'master' of github.com:vegaprotocol/frontend-monorepo
This commit is contained in:
commit
2f48a0a0a9
@ -16,7 +16,7 @@ The trading interface built based on a component toolkit. It will provide a way
|
||||
|
||||
### [Token](./apps/token)
|
||||
|
||||
The utlity dApp for interacting with the Vega token and using its' utility. This includes; delegation, nomination, governance and redemption of tokens.
|
||||
The utility dApp for interacting with the Vega token and using its' utility. This includes; delegation, nomination, governance and redemption of tokens.
|
||||
|
||||
### [Explorer](./apps/explorer)
|
||||
|
||||
@ -51,7 +51,7 @@ A utility library for connecting to the Ethereum network and interacting with Ve
|
||||
|
||||
### [React Helpers](./libs/react-helpers)
|
||||
|
||||
Generic react helpers that can be used across multilpe applications, along with other utilities.
|
||||
Generic react helpers that can be used across multiple applications, along with other utilities.
|
||||
|
||||
# 💻 Develop
|
||||
|
||||
|
26
apps/explorer-e2e/cypress.config.js
Normal file
26
apps/explorer-e2e/cypress.config.js
Normal file
@ -0,0 +1,26 @@
|
||||
const { defineConfig } = require('cypress');
|
||||
const setupNodeEvents = require('./src/plugins/index.js');
|
||||
|
||||
module.exports = defineConfig({
|
||||
projectId: 'et4snf',
|
||||
|
||||
e2e: {
|
||||
setupNodeEvents,
|
||||
baseUrl: 'http://localhost:3000',
|
||||
fileServerFolder: '.',
|
||||
fixturesFolder: false,
|
||||
specPattern: '**/*.feature',
|
||||
excludeSpecPattern: '**/*.js',
|
||||
modifyObstructiveCode: false,
|
||||
supportFile: './src/support/index.ts',
|
||||
video: true,
|
||||
videosFolder: '../../dist/cypress/apps/explorer-e2e/videos',
|
||||
screenshotsFolder: '../../dist/cypress/apps/explorer-e2e/screenshots',
|
||||
chromeWebSecurity: false,
|
||||
},
|
||||
env: {
|
||||
environment: 'CUSTOM',
|
||||
tsConfig: 'tsconfig.json',
|
||||
TAGS: 'not @todo and not @ignore and not @manual',
|
||||
},
|
||||
});
|
@ -1,21 +0,0 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:3000",
|
||||
"projectId": "et4snf",
|
||||
"fileServerFolder": ".",
|
||||
"fixturesFolder": false,
|
||||
"pluginsFile": "./src/plugins/index.js",
|
||||
"testFiles": "*.{ts,feature,features}",
|
||||
"ignoreTestFiles": "**/*.js",
|
||||
"integrationFolder": "./src/integration",
|
||||
"modifyObstructiveCode": false,
|
||||
"supportFile": "./src/support/index.ts",
|
||||
"video": true,
|
||||
"videosFolder": "../../dist/cypress/apps/explorer-e2e/videos",
|
||||
"screenshotsFolder": "../../dist/cypress/apps/explorer-e2e/screenshots",
|
||||
"chromeWebSecurity": false,
|
||||
"env": {
|
||||
"environment": "CUSTOM",
|
||||
"tsConfig": "tsconfig.json",
|
||||
"TAGS": "not @todo and not @ignore and not @manual"
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
"e2e": {
|
||||
"executor": "@nrwl/cypress:cypress",
|
||||
"options": {
|
||||
"cypressConfig": "apps/explorer-e2e/cypress.json",
|
||||
"cypressConfig": "apps/explorer-e2e/cypress.config.js",
|
||||
"devServerTarget": "explorer:serve"
|
||||
},
|
||||
"configurations": {
|
||||
|
@ -1,3 +1,5 @@
|
||||
@ignore
|
||||
# tendermint times out getting txs on testnet atm
|
||||
Feature: Transactions Page
|
||||
|
||||
Scenario: Navigate to transactions page
|
||||
@ -5,14 +7,12 @@ Feature: Transactions Page
|
||||
When I navigate to the transactions page
|
||||
Then transactions page is correctly displayed
|
||||
|
||||
@ignore
|
||||
Scenario: Navigate to transaction details page
|
||||
Given I am on the homepage
|
||||
When I navigate to the transactions page
|
||||
And I click on the top transaction
|
||||
Then transaction details are displayed
|
||||
|
||||
@ignore
|
||||
Scenario: Navigate to transactions page using mobile
|
||||
Given I am on mobile and open the toggle menu
|
||||
When I navigate to the transactions page
|
||||
|
@ -15,11 +15,9 @@ export default class TransactionsPage extends BasePage {
|
||||
txType = 'tx-type';
|
||||
|
||||
validateTransactionsPagedisplayed() {
|
||||
cy.getByTestId(this.transactionsList).should('have.length.above', 1);
|
||||
cy.getByTestId(this.blockHeight).first().should('not.be.empty');
|
||||
cy.getByTestId(this.numberOfTransactions).first().should('not.be.empty');
|
||||
cy.getByTestId(this.validatorLink).first().should('not.be.empty');
|
||||
cy.getByTestId(this.blockTime).first().should('not.be.empty');
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(5000); // Wait for transactions to load if there are any
|
||||
cy.getByTestId(this.transactionRow).should('have.length.above', 1);
|
||||
}
|
||||
|
||||
validateRefreshBtn() {
|
||||
|
@ -1,3 +1,5 @@
|
||||
export { TxList } from './tx-list';
|
||||
export { BlockTxsData } from './block-txs-data';
|
||||
export { TxOrderType } from './tx-order-type';
|
||||
export { TxsInfiniteList } from './txs-infinite-list';
|
||||
export { TxsInfiniteListItem } from './txs-infinite-list-item';
|
||||
|
@ -0,0 +1,92 @@
|
||||
import { TxsInfiniteListItem } from './txs-infinite-list-item';
|
||||
import { render, screen, fireEvent, act } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
describe('Txs infinite list item', () => {
|
||||
it('should display "missing vital data" if "Type" data missing', () => {
|
||||
render(
|
||||
<TxsInfiniteListItem
|
||||
// @ts-ignore testing deliberate failure
|
||||
Type={undefined}
|
||||
Command={'test'}
|
||||
Sig={'test'}
|
||||
PubKey={'test'}
|
||||
Nonce={1}
|
||||
TxHash={'test'}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display "missing vital data" if "Command" data missing', () => {
|
||||
render(
|
||||
<TxsInfiniteListItem
|
||||
Type={'test'}
|
||||
// @ts-ignore testing deliberate failure
|
||||
Command={undefined}
|
||||
Sig={'test'}
|
||||
PubKey={'test'}
|
||||
Nonce={1}
|
||||
TxHash={'test'}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display "missing vital data" if "Pubkey" data missing', () => {
|
||||
render(
|
||||
<TxsInfiniteListItem
|
||||
Type={'test'}
|
||||
Command={'test'}
|
||||
Sig={'test'}
|
||||
// @ts-ignore testing deliberate failure
|
||||
PubKey={undefined}
|
||||
Nonce={1}
|
||||
TxHash={'test'}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display "missing vital data" if "TxHash" data missing', () => {
|
||||
render(
|
||||
<TxsInfiniteListItem
|
||||
Type={'test'}
|
||||
Command={'test'}
|
||||
Sig={'test'}
|
||||
PubKey={'test'}
|
||||
Nonce={1}
|
||||
// @ts-ignore testing deliberate failure
|
||||
TxHash={undefined}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders data correctly', () => {
|
||||
const testCommandData = JSON.stringify({
|
||||
test: 'test of command data',
|
||||
});
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<TxsInfiniteListItem
|
||||
Type={'testType'}
|
||||
Command={testCommandData}
|
||||
Sig={'testSig'}
|
||||
PubKey={'testPubKey'}
|
||||
Nonce={1}
|
||||
TxHash={'testTxHash'}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(screen.getByTestId('tx-hash')).toHaveTextContent('testTxHash');
|
||||
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
|
||||
expect(screen.getByTestId('type')).toHaveTextContent('testType');
|
||||
const button = screen.getByTestId('command-details');
|
||||
act(() => {
|
||||
fireEvent.click(button);
|
||||
});
|
||||
expect(screen.getByText('"test of command data"')).toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -0,0 +1,70 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
Icon,
|
||||
Intent,
|
||||
SyntaxHighlighter,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { TruncatedLink } from '../truncate/truncated-link';
|
||||
import { Routes } from '../../routes/route-names';
|
||||
import { TxOrderType } from './tx-order-type';
|
||||
import type { ChainExplorerTxResponse } from '../../routes/types/chain-explorer-response';
|
||||
|
||||
const TRUNCATE_LENGTH = 14;
|
||||
|
||||
export const TxsInfiniteListItem = ({
|
||||
TxHash,
|
||||
PubKey,
|
||||
Type,
|
||||
Command,
|
||||
}: ChainExplorerTxResponse) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
if (!TxHash || !PubKey || !Type || !Command) {
|
||||
return <div>Missing vital data</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid="transaction-row"
|
||||
className="grid grid-cols-[repeat(2,_1fr)_240px] gap-12 w-full border-t border-black-60 dark:border-white-25 py-8 txs-infinite-list-item"
|
||||
>
|
||||
<div className="whitespace-nowrap overflow-scroll" data-testid="tx-hash">
|
||||
<TruncatedLink
|
||||
to={`/${Routes.TX}/${TxHash}`}
|
||||
text={TxHash}
|
||||
startChars={TRUNCATE_LENGTH}
|
||||
endChars={TRUNCATE_LENGTH}
|
||||
/>
|
||||
</div>
|
||||
<div className="whitespace-nowrap overflow-scroll" data-testid="pub-key">
|
||||
<TruncatedLink
|
||||
to={`/${Routes.PARTIES}/${PubKey}`}
|
||||
text={PubKey}
|
||||
startChars={TRUNCATE_LENGTH}
|
||||
endChars={TRUNCATE_LENGTH}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="flex justify-between whitespace-nowrap overflow-scroll"
|
||||
data-testid="type"
|
||||
>
|
||||
<TxOrderType orderType={Type} />
|
||||
<button
|
||||
title="More details"
|
||||
onClick={() => setOpen(true)}
|
||||
data-testid="command-details"
|
||||
>
|
||||
<Icon name="search-template" />
|
||||
<Dialog
|
||||
open={open}
|
||||
onChange={(isOpen) => setOpen(isOpen)}
|
||||
intent={Intent.None}
|
||||
>
|
||||
<SyntaxHighlighter data={JSON.parse(Command)} />
|
||||
</Dialog>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
134
apps/explorer/src/app/components/txs/txs-infinite-list.spec.tsx
Normal file
134
apps/explorer/src/app/components/txs/txs-infinite-list.spec.tsx
Normal file
@ -0,0 +1,134 @@
|
||||
import { TxsInfiniteList } from './txs-infinite-list';
|
||||
import { render, screen, fireEvent, act } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
const generateTxs = (number: number) => {
|
||||
return Array.from(Array(number)).map((_) => ({
|
||||
Type: 'ChainEvent',
|
||||
Command:
|
||||
'{"txId":"0xc8941ac4ea989988cb8f72e8fdab2e2009376fd17619491439d36b519d27bc93","nonce":"1494","stakingEvent":{"index":"263","block":"14805346","stakeDeposited":{"ethereumAddress":"0x2e5fe63e5d49c26998cf4bfa9b64de1cf9ae7ef2","vegaPublicKey":"657c2a8a5867c43c831e24820b7544e2fdcc1cf610cfe0ece940fe78137400fd","amount":"38471116086510047870875","blockTime":"1652968806"}}}',
|
||||
Sig: 'fe7624ab742c492cf1e667e79de4777992aca8e093c8707e1f22685c3125c6082cd21b85cd966a61ad4ca0cca2f8bed3082565caa5915bc3b2f78c1ae35cac0b',
|
||||
PubKey:
|
||||
'0x7d69327393cdfaaae50e5e215feca65273eafabfb38f32b8124e66298af346d5',
|
||||
Nonce: 18296387398179254000,
|
||||
TxHash:
|
||||
'0x9C753FA6325F7A40D9C4FA5C25E24476C54613E12B1FA2DD841E3BB00D088B77',
|
||||
}));
|
||||
};
|
||||
|
||||
describe('Txs infinite list', () => {
|
||||
it('should display a "no items" message when no items provided', () => {
|
||||
render(
|
||||
<TxsInfiniteList
|
||||
txs={undefined}
|
||||
areTxsLoading={false}
|
||||
hasMoreTxs={false}
|
||||
loadMoreTxs={() => null}
|
||||
error={undefined}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText('No items')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('error is displayed at item level', () => {
|
||||
const txs = generateTxs(1);
|
||||
render(
|
||||
<TxsInfiniteList
|
||||
txs={txs}
|
||||
areTxsLoading={false}
|
||||
hasMoreTxs={false}
|
||||
loadMoreTxs={() => null}
|
||||
error={Error('test error!')}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText('Error: test error!')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('item renders data of n length into list of n length', () => {
|
||||
// Provided the number of items doesn't exceed the 30 it initially
|
||||
// desires, all txs will initially render
|
||||
const txs = generateTxs(10);
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<TxsInfiniteList
|
||||
txs={txs}
|
||||
areTxsLoading={false}
|
||||
hasMoreTxs={false}
|
||||
loadMoreTxs={() => null}
|
||||
error={undefined}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen
|
||||
.getByTestId('infinite-scroll-wrapper')
|
||||
.querySelectorAll('.txs-infinite-list-item')
|
||||
).toHaveLength(10);
|
||||
});
|
||||
|
||||
it('tries to load more items when required to initially fill the list', () => {
|
||||
// For example, if initially rendering 15, the bottom of the list is
|
||||
// in view of the viewport, and the callback should be executed
|
||||
const txs = generateTxs(15);
|
||||
const callback = jest.fn();
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<TxsInfiniteList
|
||||
txs={txs}
|
||||
areTxsLoading={false}
|
||||
hasMoreTxs={true}
|
||||
loadMoreTxs={callback}
|
||||
error={undefined}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
expect(callback.mock.calls.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('does not try to load more items if there are no more', () => {
|
||||
const txs = generateTxs(3);
|
||||
const callback = jest.fn();
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<TxsInfiniteList
|
||||
txs={txs}
|
||||
areTxsLoading={false}
|
||||
hasMoreTxs={false}
|
||||
loadMoreTxs={callback}
|
||||
error={undefined}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
expect(callback.mock.calls.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('loads more items is called when scrolled', () => {
|
||||
const txs = generateTxs(20);
|
||||
const callback = jest.fn();
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<TxsInfiniteList
|
||||
txs={txs}
|
||||
areTxsLoading={false}
|
||||
hasMoreTxs={true}
|
||||
loadMoreTxs={callback}
|
||||
error={undefined}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
act(() => {
|
||||
fireEvent.scroll(screen.getByTestId('infinite-scroll-wrapper'), {
|
||||
target: { scrollY: 600 },
|
||||
});
|
||||
});
|
||||
|
||||
expect(callback.mock.calls.length).toEqual(1);
|
||||
});
|
||||
});
|
109
apps/explorer/src/app/components/txs/txs-infinite-list.tsx
Normal file
109
apps/explorer/src/app/components/txs/txs-infinite-list.tsx
Normal file
@ -0,0 +1,109 @@
|
||||
import React from 'react';
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
import InfiniteLoader from 'react-window-infinite-loader';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { TxsInfiniteListItem } from './txs-infinite-list-item';
|
||||
import type { ChainExplorerTxResponse } from '../../routes/types/chain-explorer-response';
|
||||
|
||||
interface TxsInfiniteListProps {
|
||||
hasMoreTxs: boolean;
|
||||
areTxsLoading: boolean | undefined;
|
||||
txs: ChainExplorerTxResponse[] | undefined;
|
||||
loadMoreTxs: () => void;
|
||||
error: Error | undefined;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
interface ItemProps {
|
||||
index: ChainExplorerTxResponse;
|
||||
style: React.CSSProperties;
|
||||
isLoading: boolean;
|
||||
error: Error | undefined;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
const NOOP = () => {};
|
||||
|
||||
const Item = ({ index, style, isLoading, error }: ItemProps) => {
|
||||
let content;
|
||||
if (error) {
|
||||
content = t(`${error}`);
|
||||
} else if (isLoading) {
|
||||
content = t('Loading...');
|
||||
} else {
|
||||
const { TxHash, PubKey, Type, Command, Sig, Nonce } = index;
|
||||
content = (
|
||||
<TxsInfiniteListItem
|
||||
Type={Type}
|
||||
Command={Command}
|
||||
Sig={Sig}
|
||||
PubKey={PubKey}
|
||||
Nonce={Nonce}
|
||||
TxHash={TxHash}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <div style={style}>{content}</div>;
|
||||
};
|
||||
|
||||
export const TxsInfiniteList = ({
|
||||
hasMoreTxs,
|
||||
areTxsLoading,
|
||||
txs,
|
||||
loadMoreTxs,
|
||||
error,
|
||||
className,
|
||||
}: TxsInfiniteListProps) => {
|
||||
if (!txs) {
|
||||
return <div>No items</div>;
|
||||
}
|
||||
|
||||
// If there are more items to be loaded then add an extra row to hold a loading indicator.
|
||||
const itemCount = hasMoreTxs ? txs.length + 1 : txs.length;
|
||||
|
||||
// Pass an empty callback to InfiniteLoader in case it asks us to load more than once.
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
const loadMoreItems = areTxsLoading ? NOOP : loadMoreTxs;
|
||||
|
||||
// Every row is loaded except for our loading indicator row.
|
||||
const isItemLoaded = (index: number) => !hasMoreTxs || index < txs.length;
|
||||
|
||||
return (
|
||||
<div className={className} data-testid="transactions-list">
|
||||
<div className="grid grid-cols-[repeat(2,_1fr)_240px] gap-12 w-full mb-8">
|
||||
<div className="text-h5 font-bold">Txn hash</div>
|
||||
<div className="text-h5 font-bold">Party</div>
|
||||
<div className="text-h5 font-bold pl-2">Type</div>
|
||||
</div>
|
||||
<div data-testid="infinite-scroll-wrapper">
|
||||
<InfiniteLoader
|
||||
isItemLoaded={isItemLoaded}
|
||||
itemCount={itemCount}
|
||||
loadMoreItems={loadMoreItems}
|
||||
>
|
||||
{({ onItemsRendered, ref }) => (
|
||||
<List
|
||||
className="List"
|
||||
height={595}
|
||||
itemCount={itemCount}
|
||||
itemSize={41}
|
||||
onItemsRendered={onItemsRendered}
|
||||
ref={ref}
|
||||
width={'100%'}
|
||||
>
|
||||
{({ index, style }) => (
|
||||
<Item
|
||||
index={txs[index]}
|
||||
style={style}
|
||||
isLoading={!isItemLoaded(index)}
|
||||
error={error}
|
||||
/>
|
||||
)}
|
||||
</List>
|
||||
)}
|
||||
</InfiniteLoader>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -12,7 +12,7 @@ import Genesis from './genesis';
|
||||
import { Block } from './blocks/id';
|
||||
import { Blocks } from './blocks/home';
|
||||
import { Tx } from './txs/id';
|
||||
import { Txs as TxHome } from './txs/home';
|
||||
import { TxsHome } from './txs/home';
|
||||
import { PendingTxs } from './pending';
|
||||
import flags from '../config/flags';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
@ -129,7 +129,7 @@ const routerConfig = [
|
||||
},
|
||||
{
|
||||
index: true,
|
||||
element: <TxHome />,
|
||||
element: <TxsHome />,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1,32 +1,122 @@
|
||||
import type { TendermintBlockchainResponse } from '../../blocks/tendermint-blockchain-response';
|
||||
import { DATA_SOURCES } from '../../../config';
|
||||
import { useCallback, useState, useMemo } from 'react';
|
||||
import { t, useFetch } from '@vegaprotocol/react-helpers';
|
||||
import { RouteTitle } from '../../../components/route-title';
|
||||
import { BlocksRefetch } from '../../../components/blocks';
|
||||
import { RenderFetched } from '../../../components/render-fetched';
|
||||
import { BlockTxsData } from '../../../components/txs';
|
||||
import { JumpToBlock } from '../../../components/jump-to-block';
|
||||
import { t, useFetch } from '@vegaprotocol/react-helpers';
|
||||
import { TxsInfiniteList } from '../../../components/txs';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ChainExplorerTxResponse } from '../../types/chain-explorer-response';
|
||||
import type { TendermintBlockchainResponse } from '../../blocks/tendermint-blockchain-response';
|
||||
|
||||
const Txs = () => {
|
||||
const {
|
||||
state: { data, error, loading },
|
||||
refetch,
|
||||
} = useFetch<TendermintBlockchainResponse>(
|
||||
`${DATA_SOURCES.tendermintUrl}/blockchain`
|
||||
interface TxsProps {
|
||||
latestBlockHeight: string;
|
||||
}
|
||||
|
||||
interface TxsStateProps {
|
||||
txsData: ChainExplorerTxResponse[];
|
||||
hasMoreTxs: boolean;
|
||||
nextPage: number;
|
||||
}
|
||||
|
||||
const Txs = ({ latestBlockHeight }: TxsProps) => {
|
||||
const [{ txsData, hasMoreTxs, nextPage }, setTxsState] =
|
||||
useState<TxsStateProps>({
|
||||
txsData: [],
|
||||
hasMoreTxs: true,
|
||||
nextPage: 1,
|
||||
});
|
||||
|
||||
const reusedBodyParams = useMemo(
|
||||
() => ({
|
||||
node_url: DATA_SOURCES.tendermintUrl,
|
||||
transaction_height: parseInt(latestBlockHeight),
|
||||
page_size: 30,
|
||||
}),
|
||||
[latestBlockHeight]
|
||||
);
|
||||
|
||||
const {
|
||||
state: { error, loading },
|
||||
refetch,
|
||||
} = useFetch(
|
||||
DATA_SOURCES.chainExplorerUrl,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify(reusedBodyParams),
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
const loadTxs = useCallback(async () => {
|
||||
const data = await refetch(
|
||||
undefined,
|
||||
JSON.stringify({
|
||||
...reusedBodyParams,
|
||||
page_number: nextPage,
|
||||
})
|
||||
);
|
||||
|
||||
if (data) {
|
||||
setTxsState((prev) => ({
|
||||
...prev,
|
||||
nextPage: prev.nextPage + 1,
|
||||
hasMoreTxs: true,
|
||||
txsData: [...prev.txsData, ...(data as ChainExplorerTxResponse[])],
|
||||
}));
|
||||
}
|
||||
}, [nextPage, refetch, reusedBodyParams]);
|
||||
|
||||
return (
|
||||
<section>
|
||||
<RouteTitle>{t('Transactions')}</RouteTitle>
|
||||
<RenderFetched error={error} loading={loading}>
|
||||
<>
|
||||
<BlocksRefetch refetch={refetch} />
|
||||
<BlockTxsData data={data} />
|
||||
</>
|
||||
</RenderFetched>
|
||||
<BlocksRefetch
|
||||
refetch={() =>
|
||||
refetch(
|
||||
undefined,
|
||||
JSON.stringify({
|
||||
...reusedBodyParams,
|
||||
page_number: 1,
|
||||
})
|
||||
)
|
||||
}
|
||||
/>
|
||||
<TxsInfiniteList
|
||||
hasMoreTxs={hasMoreTxs}
|
||||
areTxsLoading={loading}
|
||||
txs={txsData}
|
||||
loadMoreTxs={loadTxs}
|
||||
error={error}
|
||||
className="mb-28"
|
||||
/>
|
||||
<JumpToBlock />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export { Txs };
|
||||
const Wrapper = () => {
|
||||
const {
|
||||
state: { data, error, loading },
|
||||
} = useFetch<TendermintBlockchainResponse>(
|
||||
`${DATA_SOURCES.tendermintUrl}/blockchain`
|
||||
);
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={!!loading}
|
||||
loadingMessage={t('Getting latest block height...')}
|
||||
error={error}
|
||||
data={data}
|
||||
noDataMessage={t('Could not get latest block height')}
|
||||
render={(data) => (
|
||||
<Txs
|
||||
latestBlockHeight={
|
||||
data?.result?.block_metas?.[0]?.header?.height || ''
|
||||
}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export { Wrapper as TxsHome };
|
||||
|
18
apps/simple-trading-app-e2e/cypress.config.js
Normal file
18
apps/simple-trading-app-e2e/cypress.config.js
Normal file
@ -0,0 +1,18 @@
|
||||
const { defineConfig } = require('cypress');
|
||||
module.exports = defineConfig({
|
||||
projectId: 'et4snf',
|
||||
|
||||
e2e: {
|
||||
baseUrl: 'http://localhost:4200',
|
||||
fileServerFolder: '.',
|
||||
fixturesFolder: false,
|
||||
specPattern: './src/integration/*.ts',
|
||||
excludeSpecPattern: '**/*.js',
|
||||
modifyObstructiveCode: false,
|
||||
supportFile: './src/support/index.ts',
|
||||
video: true,
|
||||
videosFolder: '../../dist/cypress/apps/explorer-e2e/videos',
|
||||
screenshotsFolder: '../../dist/cypress/apps/explorer-e2e/screenshots',
|
||||
chromeWebSecurity: false,
|
||||
},
|
||||
});
|
@ -1,16 +0,0 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:4200",
|
||||
"fileServerFolder": ".",
|
||||
"testFiles": "*.{ts,feature,features}",
|
||||
"ignoreTestFiles": "**/*.js",
|
||||
"fixturesFolder": "./src/fixtures",
|
||||
"integrationFolder": "./src/integration",
|
||||
"modifyObstructiveCode": false,
|
||||
"supportFile": "./src/support/index.ts",
|
||||
"pluginsFile": false,
|
||||
"video": true,
|
||||
"videosFolder": "../../dist/cypress/apps/simple-trading-app-e2e/videos",
|
||||
"screenshotsFolder": "../../dist/cypress/apps/simple-trading-app-e2e/screenshots",
|
||||
"chromeWebSecurity": false,
|
||||
"projectId": "et4snf"
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
"e2e": {
|
||||
"executor": "@nrwl/cypress:cypress",
|
||||
"options": {
|
||||
"cypressConfig": "apps/simple-trading-app-e2e/cypress.json",
|
||||
"cypressConfig": "apps/simple-trading-app-e2e/cypress.config.js",
|
||||
"devServerTarget": "simple-trading-app:serve"
|
||||
},
|
||||
"configurations": {
|
||||
|
@ -5,7 +5,10 @@ describe('market list', () => {
|
||||
});
|
||||
|
||||
it('selects menus', () => {
|
||||
cy.get('.MuiDrawer-root [aria-current]').should('have.text', 'Markets');
|
||||
cy.get('[aria-label="Sidebar Navigation Menu"] [aria-current]').should(
|
||||
'have.text',
|
||||
'Markets'
|
||||
);
|
||||
cy.getByTestId('state-trigger').should('have.text', 'Active');
|
||||
cy.get('[aria-label="Future"]').click();
|
||||
cy.get('[data-testid="market-assets-menu"] a.active').should(
|
||||
@ -57,7 +60,7 @@ describe('market list', () => {
|
||||
cy.intercept('POST', '/query').as('Filters');
|
||||
cy.visit('/markets');
|
||||
cy.wait('@Filters').then((filters) => {
|
||||
if (filters?.response?.body.data.markets.length) {
|
||||
if (filters?.response?.body?.data?.markets?.length) {
|
||||
const asset =
|
||||
filters.response.body.data.markets[0].tradableInstrument.instrument
|
||||
.product.settlementAsset.symbol;
|
||||
|
@ -40,13 +40,7 @@ function App() {
|
||||
<VegaWalletProvider>
|
||||
<AppLoader>
|
||||
<div className="max-h-full min-h-full dark:bg-black dark:text-white-60 bg-white text-black-60 grid grid-rows-[min-content,1fr]">
|
||||
<div className="flex items-stretch border-b-[7px] border-vega-yellow">
|
||||
<DrawerToggle
|
||||
onToggle={onToggle}
|
||||
variant={DRAWER_TOGGLE_VARIANTS.OPEN}
|
||||
className="xs:py-32 xs:px-16"
|
||||
/>
|
||||
|
||||
<div className="flex items-stretch p-16 bg-black text-white-60">
|
||||
<div className="flex items-center gap-4 ml-auto mr-8">
|
||||
<VegaWalletConnectButton
|
||||
setConnectDialog={(open) =>
|
||||
@ -56,8 +50,17 @@ function App() {
|
||||
setVegaWallet((x) => ({ ...x, manage: open }))
|
||||
}
|
||||
/>
|
||||
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" />
|
||||
<ThemeSwitcher
|
||||
onToggle={toggleTheme}
|
||||
className="-my-4"
|
||||
sunClassName="text-white"
|
||||
/>
|
||||
</div>
|
||||
<DrawerToggle
|
||||
onToggle={onToggle}
|
||||
variant={DRAWER_TOGGLE_VARIANTS.OPEN}
|
||||
className="xs:py-32 xs:px-16 xs:text-white xs:hover:text-blue"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Main isMenuOpen={menuOpen} onToggle={onToggle} />
|
||||
|
@ -1,14 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
interface Props {
|
||||
children: ReactElement | ReactElement[];
|
||||
}
|
||||
|
||||
export const DrawerContainer = ({ children }: Props) => (
|
||||
<div className="w-full md:w-3/4 grow-1">{children}</div>
|
||||
);
|
||||
|
||||
export const DrawerWrapper = ({ children }: Props) => (
|
||||
<div className="flex dark:bg-black">{children}</div>
|
||||
);
|
@ -0,0 +1,21 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
interface Props {
|
||||
children: ReactElement | ReactElement[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const DrawerContent = ({ children, className = '' }: Props) => {
|
||||
const classes = classNames(
|
||||
'w-full sm:w-full grow-1 p-20 overflow-hidden',
|
||||
className
|
||||
);
|
||||
|
||||
return (
|
||||
<main aria-label="Page Content" className={classes}>
|
||||
{children}
|
||||
</main>
|
||||
);
|
||||
};
|
@ -1,38 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import type { ReactElement } from 'react';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ButtonProps } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
type MenuItem = {
|
||||
label: string;
|
||||
component: ReactElement | ReactElement[];
|
||||
onClick(): void;
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* Menu items passed as an array
|
||||
*/
|
||||
menuItems: MenuItem[];
|
||||
}
|
||||
|
||||
export const NavigationDrawerMenu = ({ menuItems }: Props) => {
|
||||
return (
|
||||
<ul>
|
||||
{menuItems.map((item, index) => {
|
||||
const btnProps = {
|
||||
variant: item.active ? 'accent' : 'primary',
|
||||
className: 'w-full mb-8',
|
||||
onClick: item.onClick,
|
||||
} as ButtonProps;
|
||||
|
||||
return (
|
||||
<li key={index}>
|
||||
<Button {...btnProps}>{item.label}</Button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
};
|
@ -19,12 +19,10 @@ interface Props {
|
||||
export const DrawerToggle = ({
|
||||
onToggle,
|
||||
variant = DRAWER_TOGGLE_VARIANTS.CLOSE,
|
||||
className,
|
||||
className = '',
|
||||
}: Props) => {
|
||||
const [iconName, setIconName] = useState(IconNames.MENU);
|
||||
const classes = classNames('md:hidden', {
|
||||
[className as string]: className,
|
||||
});
|
||||
const classes = classNames('md:hidden', className);
|
||||
|
||||
useEffect(() => {
|
||||
if (variant === DRAWER_TOGGLE_VARIANTS.OPEN) {
|
||||
@ -34,8 +32,17 @@ export const DrawerToggle = ({
|
||||
}
|
||||
}, [variant]);
|
||||
|
||||
const ariaLabel = `${
|
||||
variant === DRAWER_TOGGLE_VARIANTS.OPEN ? 'Open' : 'Close'
|
||||
} Sidebar Navigation Menu`;
|
||||
|
||||
return (
|
||||
<Button variant="inline-link" className={classes} onClick={onToggle}>
|
||||
<Button
|
||||
aria-label={ariaLabel}
|
||||
variant="inline-link"
|
||||
className={classes}
|
||||
onClick={onToggle}
|
||||
>
|
||||
<Icon name={iconName as IconName} />
|
||||
</Button>
|
||||
);
|
||||
|
@ -0,0 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
interface Props {
|
||||
children: ReactElement | ReactElement[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const DrawerWrapper = ({ children, className = '' }: Props) => {
|
||||
const classes = classNames('flex dark:bg-black md:flex-row', className);
|
||||
return <div className={classes}>{children}</div>;
|
||||
};
|
@ -1,66 +1,60 @@
|
||||
import * as React from 'react';
|
||||
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import Drawer from '@mui/material/Drawer';
|
||||
|
||||
interface Props {
|
||||
children?: ReactElement | ReactElement[];
|
||||
isMenuOpen?: boolean;
|
||||
onToggle(): void;
|
||||
rtl?: boolean;
|
||||
outerClasses?: string;
|
||||
innerClasses?: string;
|
||||
}
|
||||
|
||||
export const NavigationDrawer = ({
|
||||
isMenuOpen = false,
|
||||
onToggle,
|
||||
children,
|
||||
rtl,
|
||||
outerClasses = '',
|
||||
innerClasses = '',
|
||||
}: Props) => {
|
||||
const [windowSize, setWindowSize] = React.useState({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
const width = 'w-full md:w-auto md:min-w-[15%] shrink-0';
|
||||
const position = 'absolute inset-0 h-full z-10 md:static';
|
||||
const background = 'bg-black/50 dark:bg-white/50';
|
||||
const flex = 'flex justify-end overflow-hidden';
|
||||
const joinedClasses = [flex, width, position, background].join(' ');
|
||||
|
||||
const outerStyles = classNames(joinedClasses, {
|
||||
visible: isMenuOpen,
|
||||
'invisible md:visible': !isMenuOpen,
|
||||
'flex-row-reverse': !rtl,
|
||||
[outerClasses]: outerClasses,
|
||||
});
|
||||
const mobileScreenWidth = parseInt(theme.screens.md);
|
||||
const isMobile = windowSize.width <= mobileScreenWidth;
|
||||
const timeout = React.useRef(0);
|
||||
|
||||
const handleResize = () => {
|
||||
if (timeout.current) {
|
||||
window.cancelAnimationFrame(timeout.current);
|
||||
}
|
||||
const translateClose = rtl ? 'translate-x-full' : '-translate-x-full';
|
||||
|
||||
// Setup the new requestAnimationFrame()
|
||||
timeout.current = window.requestAnimationFrame(function () {
|
||||
setWindowSize({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
const innerStyles = classNames('w-3/4 md:w-full bg-white dark:bg-black', {
|
||||
'translate-x-0 transition-transform md:transform-none': isMenuOpen,
|
||||
[`${translateClose} md:transform-none`]: !isMenuOpen,
|
||||
[innerClasses]: innerClasses,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
window.cancelAnimationFrame(timeout.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const drawerRootClasses = {
|
||||
root: 'w-3/4 md:w-1/4 shrink-0',
|
||||
paper: 'p-16 w-3/4 md:w-1/4 box-border',
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
classes={drawerRootClasses}
|
||||
variant={isMobile ? 'temporary' : 'permanent'}
|
||||
open={isMobile ? isMenuOpen : true}
|
||||
onClose={onToggle}
|
||||
ModalProps={{
|
||||
keepMounted: true, // Better open performance on mobile.
|
||||
}}
|
||||
<aside aria-label="Sidebar Navigation Menu" className={outerStyles}>
|
||||
<div
|
||||
role="presentation"
|
||||
aria-label="Content Overlay - Click To Close Sidebar Navigation"
|
||||
className="md:hidden grow h-full"
|
||||
onClick={onToggle}
|
||||
/>
|
||||
<div
|
||||
role="group"
|
||||
aria-label="Sidebar Navigation Grouped Content"
|
||||
className={innerStyles}
|
||||
>
|
||||
{children}
|
||||
</Drawer>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
export * from './drawer';
|
||||
export * from './drawer-menu';
|
||||
export * from './drawer-toggle';
|
||||
export * from './drawer-container';
|
||||
export * from './drawer-content';
|
||||
export * from './drawer-wrapper';
|
||||
|
51
apps/simple-trading-app/src/app/components/icons/icon.tsx
Normal file
51
apps/simple-trading-app/src/app/components/icons/icon.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import type { ReactElement } from 'react';
|
||||
import { LiquidityIconPath } from './liquidity-icon';
|
||||
import { MarketIconPath } from './market-icon';
|
||||
import { TradeIconPath } from './trade-icon';
|
||||
import { PortfolioIconPath } from './portfolio-icon';
|
||||
|
||||
interface IconProps {
|
||||
name: string;
|
||||
className: string;
|
||||
}
|
||||
|
||||
interface IconSVGWrapperProps {
|
||||
className: string;
|
||||
children: ReactElement | ReactElement[] | null;
|
||||
}
|
||||
|
||||
const getIconPath = (name: string): ReactElement | null => {
|
||||
switch (name) {
|
||||
case 'liquidity':
|
||||
return <LiquidityIconPath />;
|
||||
case 'market':
|
||||
return <MarketIconPath />;
|
||||
case 'trade':
|
||||
return <TradeIconPath />;
|
||||
case 'portfolio':
|
||||
return <PortfolioIconPath />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const IconSVGWrapper = ({ className, children }: IconSVGWrapperProps) => {
|
||||
return (
|
||||
<svg
|
||||
role="presentation"
|
||||
className={className}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
>
|
||||
{children}
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const Icon = ({ name, className }: IconProps) => {
|
||||
return (
|
||||
<IconSVGWrapper className={className}>{getIconPath(name)}</IconSVGWrapper>
|
||||
);
|
||||
};
|
@ -0,0 +1 @@
|
||||
export * from './icon';
|
@ -0,0 +1,15 @@
|
||||
export const LiquidityIconPath = () => (
|
||||
<>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12.5268 1.46612L12 1.99999L11.4732 1.46612L12 0.946381L12.5268 1.46612ZM12 3.07542C11.8274 3.25709 11.6185 3.48044 11.3826 3.73943C10.6899 4.49987 9.76793 5.56335 8.84782 6.77789C7.92587 7.99485 7.01715 9.34862 6.34179 10.6903C5.66046 12.0439 5.25 13.3205 5.25 14.4C5.25 19.0485 8.47431 22.15 12 22.15C15.5257 22.15 18.75 19.0485 18.75 14.4C18.75 13.3205 18.3395 12.0439 17.6582 10.6903C16.9829 9.34862 16.0741 7.99485 15.1522 6.77789C14.2321 5.56335 13.3101 4.49987 12.6174 3.73943C12.3815 3.48044 12.1726 3.25709 12 3.07542ZM11.4732 1.46612C11.4734 1.46597 11.4732 1.46612 12 1.99999C12.5268 1.46612 12.5266 1.46597 12.5268 1.46612L12.5336 1.47291L12.5512 1.49036C12.5663 1.5055 12.5884 1.52759 12.6169 1.55634C12.6739 1.61384 12.7566 1.698 12.8615 1.80641C13.071 2.02319 13.3692 2.33722 13.7263 2.72931C14.4399 3.51261 15.3929 4.61164 16.3478 5.8721C17.3009 7.13013 18.2671 8.56386 18.998 10.0159C19.723 11.4561 20.25 12.9795 20.25 14.4C20.25 19.7514 16.4743 23.65 12 23.65C7.52569 23.65 3.75 19.7514 3.75 14.4C3.75 12.9795 4.27704 11.4561 5.00196 10.0159C5.73285 8.56386 6.69913 7.13013 7.65218 5.8721C8.60707 4.61164 9.56014 3.51261 10.2737 2.72931C10.6308 2.33722 10.929 2.02319 11.1385 1.80641C11.2434 1.698 11.3261 1.61384 11.3831 1.55634C11.4116 1.52759 11.4337 1.5055 11.4488 1.49036L11.4664 1.47291L11.4712 1.46817L11.4732 1.46612Z"
|
||||
/>
|
||||
<rect x="11.5" y="17.4" width="1" height="1" />
|
||||
<rect x="12.5" y="16.4" width="1" height="1" />
|
||||
<rect x="10.5" y="16.4" width="1" height="1" />
|
||||
<rect x="14.5" y="15.4" width="1" height="1" />
|
||||
<rect x="13.5" y="11.4" width="1" height="4" />
|
||||
<rect x="9.5" y="11.4" width="1" height="5" />
|
||||
</>
|
||||
);
|
@ -0,0 +1,17 @@
|
||||
export const MarketIconPath = () => (
|
||||
<>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M3.5 2H2V20.5V22H3.5H22V20.5H3.5V2Z"
|
||||
/>
|
||||
<rect x="5" y="15" width="2" height="2" />
|
||||
<rect x="7" y="13" width="2" height="2" />
|
||||
<rect x="9" y="11" width="2" height="2" />
|
||||
<rect x="11" y="9" width="2" height="2" />
|
||||
<rect x="13" y="11" width="2" height="2" />
|
||||
<rect x="15" y="9" width="2" height="2" />
|
||||
<rect x="17" y="7" width="2" height="2" />
|
||||
<rect x="19" y="5" width="2" height="2" />
|
||||
</>
|
||||
);
|
@ -0,0 +1,17 @@
|
||||
export const PortfolioIconPath = () => (
|
||||
<>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M8.5 3.5H15.5V6H8.5V3.5ZM7 6V3.5C7 2.67157 7.67157 2 8.5 2H15.5C16.3284 2 17 2.67157 17 3.5V6H20.5C21.3284 6 22 6.67157 22 7.5V20.5C22 21.3284 21.3284 22 20.5 22H3.5C2.67157 22 2 21.3284 2 20.5V7.5C2 6.67157 2.67157 6 3.5 6H7ZM15.5 7.5H8.5H3.5L3.5 20.5H20.5V7.5H15.5Z"
|
||||
/>
|
||||
<rect x="4" y="8" width="2" height="2" />
|
||||
<rect x="6" y="10" width="2" height="2" />
|
||||
<rect x="18" y="8" width="2" height="2" />
|
||||
<rect x="16" y="10" width="2" height="2" />
|
||||
<rect x="14" y="12" width="2" height="2" />
|
||||
<rect x="8" y="12" width="2" height="2" />
|
||||
<rect x="10" y="14" width="2" height="2" />
|
||||
<rect x="12" y="14" width="2" height="2" />
|
||||
</>
|
||||
);
|
@ -0,0 +1,14 @@
|
||||
export const TradeIconPath = () => (
|
||||
<>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M19.3254 7.28784L14.8547 2.93099L15.7621 1.99999L21.4056 7.49983L21.8833 7.96534L21.4056 8.43085L15.7621 13.9307L14.8547 12.9997L19.3819 8.58784L1.99999 8.58784L1.99999 7.28784L19.3254 7.28784Z"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M4.55785 16.6429L9.02855 20.9997L8.12124 21.9307L2.47767 16.4309L2 15.9654L2.47767 15.4999L8.12124 10L9.02855 10.931L4.50141 15.3429L21.8833 15.3429V16.6429L4.55785 16.6429Z"
|
||||
/>
|
||||
</>
|
||||
);
|
@ -1,62 +1,41 @@
|
||||
import React from 'react';
|
||||
import { useRoutes, NavLink } from 'react-router-dom';
|
||||
import { useRoutes } from 'react-router-dom';
|
||||
import {
|
||||
NavigationDrawer,
|
||||
DrawerWrapper,
|
||||
DrawerContainer,
|
||||
DrawerContent,
|
||||
DrawerToggle,
|
||||
DRAWER_TOGGLE_VARIANTS,
|
||||
} from '../drawer';
|
||||
import { Nav, TabBar } from '../nav';
|
||||
import { routerConfig } from '../../routes/router-config';
|
||||
|
||||
export interface RouteChildProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const AppRouter = () => {
|
||||
const routes = useRoutes(routerConfig);
|
||||
|
||||
return <main className="p-20 overflow-hidden">{routes}</main>;
|
||||
};
|
||||
|
||||
export const Menu = () => (
|
||||
<nav>
|
||||
{routerConfig.map((r) => (
|
||||
<NavLink
|
||||
key={r.name}
|
||||
to={r.path}
|
||||
className={({ isActive }) =>
|
||||
`text-h5 block mb-8 px-8 hover:bg-vega-yellow hover:text-black ${
|
||||
isActive && 'bg-vega-yellow text-black'
|
||||
}`
|
||||
}
|
||||
>
|
||||
{r.text}
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
export const AppRouter = () => useRoutes(routerConfig);
|
||||
|
||||
interface Props {
|
||||
onToggle(): void;
|
||||
|
||||
isMenuOpen: boolean;
|
||||
}
|
||||
|
||||
export const Main = ({ onToggle, isMenuOpen }: Props) => {
|
||||
return (
|
||||
<DrawerWrapper>
|
||||
<NavigationDrawer onToggle={onToggle} isMenuOpen={isMenuOpen}>
|
||||
<NavigationDrawer rtl onToggle={onToggle} isMenuOpen={isMenuOpen}>
|
||||
<DrawerToggle
|
||||
onToggle={onToggle}
|
||||
variant={DRAWER_TOGGLE_VARIANTS.CLOSE}
|
||||
className="self-end p-16"
|
||||
className="p-16"
|
||||
/>
|
||||
<Menu />
|
||||
<Nav className="hidden md:block my-20 h-full" />
|
||||
</NavigationDrawer>
|
||||
<DrawerContainer>
|
||||
<DrawerContent>
|
||||
<AppRouter />
|
||||
</DrawerContainer>
|
||||
<TabBar className="md:hidden" />
|
||||
</DrawerContent>
|
||||
</DrawerWrapper>
|
||||
);
|
||||
};
|
||||
|
2
apps/simple-trading-app/src/app/components/nav/index.tsx
Normal file
2
apps/simple-trading-app/src/app/components/nav/index.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './nav';
|
||||
export * from './tab-bar';
|
16
apps/simple-trading-app/src/app/components/nav/nav-item.tsx
Normal file
16
apps/simple-trading-app/src/app/components/nav/nav-item.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { Icon } from '../icons';
|
||||
|
||||
interface NavItemProps {
|
||||
iconName: string;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export const NavItem = ({ iconName, label }: NavItemProps) => {
|
||||
return (
|
||||
<div className="flex flex-col md:flex-row items-center justify-start cursor-pointer relative">
|
||||
<Icon name={iconName} className="mr-8" />
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
30
apps/simple-trading-app/src/app/components/nav/nav.tsx
Normal file
30
apps/simple-trading-app/src/app/components/nav/nav.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { routerConfig } from '../../routes';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { NavItem } from './nav-item';
|
||||
import React from 'react';
|
||||
|
||||
interface NavProps {
|
||||
className?: string;
|
||||
tabs?: boolean;
|
||||
}
|
||||
|
||||
export const Nav = ({ className, tabs = false }: NavProps) => {
|
||||
return (
|
||||
<nav role={tabs ? 'tablist' : 'menu'} className={className}>
|
||||
{routerConfig.map((r) => (
|
||||
<NavLink
|
||||
role={tabs ? 'tab' : 'menuitem'}
|
||||
key={r.name}
|
||||
to={r.path}
|
||||
className={({ isActive }) =>
|
||||
`text-h5 block md:mb-40 px-40 md:text-black md:dark:text-white ${
|
||||
isActive && 'text-white md:text-blue md:dark:text-blue'
|
||||
}`
|
||||
}
|
||||
>
|
||||
<NavItem iconName={r.icon} label={r.text} />
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
};
|
15
apps/simple-trading-app/src/app/components/nav/tab-bar.tsx
Normal file
15
apps/simple-trading-app/src/app/components/nav/tab-bar.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { Nav } from './nav';
|
||||
import React from 'react';
|
||||
|
||||
interface TabBarProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const TabBar = ({ className }: TabBarProps) => (
|
||||
<div role="group" aria-label="Tab Bar Navigation Menu" className={className}>
|
||||
<div role="presentation" className="py-[42px]" />
|
||||
<div className="md:hidden fixed bottom-0 left-0 right-0 bg-black py-16">
|
||||
<Nav tabs className="flex justify-evenly items-center" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
@ -47,6 +47,10 @@ export interface SimpleMarkets_markets_tradableInstrument_instrument_product_set
|
||||
|
||||
export interface SimpleMarkets_markets_tradableInstrument_instrument_product {
|
||||
__typename: "Future";
|
||||
/**
|
||||
* String representing the quote (e.g. BTCUSD -> USD is quote)
|
||||
*/
|
||||
quoteName: string;
|
||||
/**
|
||||
* The name of the asset (string)
|
||||
*/
|
||||
|
@ -1,19 +1,5 @@
|
||||
import { Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const MARKET_STATUS: Record<MarketState | '', Intent> = {
|
||||
[MarketState.Active]: Intent.Success,
|
||||
[MarketState.Cancelled]: Intent.Primary,
|
||||
[MarketState.Closed]: Intent.None,
|
||||
[MarketState.Pending]: Intent.Warning,
|
||||
[MarketState.Proposed]: Intent.Warning,
|
||||
[MarketState.Rejected]: Intent.Danger,
|
||||
[MarketState.Settled]: Intent.Primary,
|
||||
[MarketState.Suspended]: Intent.Warning,
|
||||
[MarketState.TradingTerminated]: Intent.Danger,
|
||||
'': Intent.Primary,
|
||||
};
|
||||
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
|
||||
|
||||
export const STATES_FILTER = [
|
||||
{ value: 'all', text: t('All') },
|
||||
@ -27,3 +13,115 @@ export const STATES_FILTER = [
|
||||
{ value: 'Suspended', text: t('Suspended') },
|
||||
{ value: 'TradingTerminated', text: t('TradingTerminated') },
|
||||
];
|
||||
|
||||
export const agGridLightVariables = `
|
||||
.ag-theme-balham {
|
||||
--ag-row-border-color: ${theme.colors.transparent};
|
||||
--ag-row-hover-color: ${theme.colors.transparent};
|
||||
--ag-font-size: 15px;
|
||||
}
|
||||
.ag-theme-balham .ag-row-hover {
|
||||
--ag-row-border-color: ${theme.colors.black[100]};
|
||||
}
|
||||
.ag-theme-balham [col-id="status"] .ag-header-cell-label,
|
||||
.ag-theme-balham [col-id="asset"] .ag-header-cell-label,
|
||||
.ag-theme-balham [col-id="change"] .ag-header-cell-label{
|
||||
justify-content: center;
|
||||
}
|
||||
.ag-theme-balham .ag-header-row .ag-header-cell:first-child{
|
||||
padding-left: 0;
|
||||
}
|
||||
.ag-theme-balham .ag-ltr .ag-header-cell::after, .ag-theme-balham .ag-ltr .ag-header-group-cell::after {
|
||||
right: 0;
|
||||
}
|
||||
.ag-theme-balham .ag-header-cell::after{
|
||||
width: 0;
|
||||
}
|
||||
.ag-theme-balham .ag-header{
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.ag-theme-balham .ag-has-focus .ag-row.ag-row-focus .ag-cell-focus {
|
||||
outline: none;
|
||||
border-width: 0;
|
||||
}
|
||||
.ag-theme-balham .ag-header-label-icon .ag-icon{
|
||||
position: relative;
|
||||
}
|
||||
.ag-theme-balham .ag-icon::before{
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
position: absolute;
|
||||
transform: rotate(45deg);
|
||||
top: -6px;
|
||||
right: -14px;
|
||||
content: "◾";
|
||||
background: -webkit-linear-gradient(135deg, rgba(0,0,0,0.54) 0%, rgba(0,0,0,0.54) 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, rgba(0,0,0,0.54) 52%, rgba(0,0,0,0.54) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.ag-theme-balham .ag-icon-desc::before{
|
||||
background: -webkit-linear-gradient(135deg, #000 0%, #000 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, rgba(0,0,0,0.54) 52%, rgba(0,0,0,0.54) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.ag-theme-balham .ag-icon-asc::before{
|
||||
background: -webkit-linear-gradient(135deg, rgba(0,0,0,0.54) 0%, rgba(0,0,0,0.54) 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, #000 52%, #000 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
`;
|
||||
|
||||
export const agGridDarkVariables = `
|
||||
.ag-theme-balham-dark {
|
||||
--ag-row-border-color: ${theme.colors.transparent};
|
||||
--ag-row-hover-color: ${theme.colors.transparent};
|
||||
--ag-font-size: 15px;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-row-hover {
|
||||
--ag-row-border-color: ${theme.colors.white[100]};
|
||||
}
|
||||
.ag-theme-balham-dark [col-id="status"] .ag-header-cell-label,
|
||||
.ag-theme-balham-dark [col-id="asset"] .ag-header-cell-label,
|
||||
.ag-theme-balham-dark [col-id="change"] .ag-header-cell-label{
|
||||
justify-content: center;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-header-row .ag-header-cell:first-child{
|
||||
padding-left: 0;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-header-cell::after{
|
||||
width: 0;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-header{
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-has-focus .ag-row.ag-row-focus .ag-cell-focus {
|
||||
outline: none;
|
||||
border-width: 0;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-header-label-icon .ag-icon{
|
||||
position: relative;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-icon::before{
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
position: absolute;
|
||||
transform: rotate(45deg);
|
||||
top: -6px;
|
||||
right: -14px;
|
||||
content: "◾";
|
||||
background: -webkit-linear-gradient(135deg, rgba(245, 245, 245, 0.64) 0%, rgba(245, 245, 245, 0.64) 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, rgba(245, 245, 245, 0.64) 52%, rgba(245, 245, 245, 0.64) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-position: center;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-icon-desc::before{
|
||||
background: -webkit-linear-gradient(135deg, #fff 0%, #fff 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, rgba(245, 245, 245, 0.64) 52%, rgba(245, 245, 245, 0.64) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-icon-asc::before{
|
||||
background: -webkit-linear-gradient(135deg, rgba(245, 245, 245, 0.64) 0%, rgba(245, 245, 245, 0.64) 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, #fff 52%, #fff 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
`;
|
||||
|
@ -36,6 +36,7 @@ export const MARKETS_QUERY = gql`
|
||||
product {
|
||||
__typename
|
||||
... on Future {
|
||||
quoteName
|
||||
settlementAsset {
|
||||
symbol
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import {
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
cleanup,
|
||||
getAllByRole,
|
||||
} from '@testing-library/react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
@ -37,6 +43,7 @@ describe('SimpleMarketList', () => {
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('should be properly renderer as empty', async () => {
|
||||
@ -130,8 +137,12 @@ describe('SimpleMarketList', () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('simple-market-list')).toBeInTheDocument();
|
||||
expect(
|
||||
document.querySelector('.ag-center-cols-container')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByTestId('simple-market-list').children).toHaveLength(2);
|
||||
|
||||
const container = document.querySelector('.ag-center-cols-container');
|
||||
expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
@ -1,17 +1,29 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { subDays } from 'date-fns';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { AsyncRenderer, Lozenge, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import type { MarketState } from '@vegaprotocol/types';
|
||||
import SimpleMarketPercentChange from './simple-market-percent-change';
|
||||
import SimpleMarketExpires from './simple-market-expires';
|
||||
import DataProvider from './data-provider';
|
||||
import { MARKET_STATUS } from './constants';
|
||||
import SimpleMarketToolbar from './simple-market-toolbar';
|
||||
import useMarketsFilterData from '../../hooks/use-markets-filter-data';
|
||||
import useColumnDefinitions from '../../hooks/use-column-definitions';
|
||||
import DataProvider from './data-provider';
|
||||
import * as constants from './constants';
|
||||
import SimpleMarketToolbar from './simple-market-toolbar';
|
||||
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
|
||||
|
||||
export type SimpleMarketsType = SimpleMarkets_markets & {
|
||||
percentChange?: number | '-';
|
||||
};
|
||||
|
||||
export type RouterParams = Partial<{
|
||||
product: string;
|
||||
@ -22,8 +34,9 @@ export type RouterParams = Partial<{
|
||||
const SimpleMarketList = () => {
|
||||
const navigate = useNavigate();
|
||||
const params = useParams<RouterParams>();
|
||||
|
||||
const theme = useContext(ThemeContext);
|
||||
const statusesRef = useRef<Record<string, MarketState | ''>>({});
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
CandleSince: subDays(Date.now(), 1).toJSON(),
|
||||
@ -40,7 +53,14 @@ const SimpleMarketList = () => {
|
||||
update,
|
||||
variables
|
||||
);
|
||||
const localData = useMarketsFilterData(data || [], params);
|
||||
const localData: Array<SimpleMarketsType> = useMarketsFilterData(
|
||||
data || [],
|
||||
params
|
||||
);
|
||||
|
||||
const handleOnGridReady = useCallback(() => {
|
||||
gridRef.current?.api.sizeColumnsToFit();
|
||||
}, [gridRef]);
|
||||
|
||||
useEffect(() => {
|
||||
const statuses: Record<string, MarketState | ''> = {};
|
||||
@ -50,6 +70,11 @@ const SimpleMarketList = () => {
|
||||
statusesRef.current = statuses;
|
||||
}, [data, statusesRef]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', handleOnGridReady);
|
||||
return () => window.removeEventListener('resize', handleOnGridReady);
|
||||
}, [handleOnGridReady]);
|
||||
|
||||
const onClick = useCallback(
|
||||
(marketId) => {
|
||||
navigate(`/trading/${marketId}`);
|
||||
@ -57,62 +82,34 @@ const SimpleMarketList = () => {
|
||||
[navigate]
|
||||
);
|
||||
|
||||
const { columnDefs, defaultColDef } = useColumnDefinitions({ onClick });
|
||||
|
||||
const getRowId = useCallback(({ data }) => data.id, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="h-full grid grid-rows-[min-content,1fr]">
|
||||
<SimpleMarketToolbar />
|
||||
<AsyncRenderer loading={loading} error={error} data={localData}>
|
||||
{localData && localData.length > 0 ? (
|
||||
<ul
|
||||
className="list-none relative pt-8 pb-8"
|
||||
data-testid="simple-market-list"
|
||||
>
|
||||
{localData?.map((market) => (
|
||||
<li
|
||||
className="w-full relative flex justify-start items-center no-underline box-border text-left py-8 mb-10"
|
||||
key={market.id}
|
||||
>
|
||||
<div className="w-full grid sm:grid-cols-2">
|
||||
<div className="w-full grid sm:auto-rows-auto">
|
||||
<div className="font-extrabold py-2">{market.name}</div>
|
||||
<SimpleMarketExpires
|
||||
tags={market.tradableInstrument.instrument.metadata.tags}
|
||||
<AgGrid
|
||||
className="mb-32 min-h-[300px]"
|
||||
defaultColDef={defaultColDef}
|
||||
columnDefs={columnDefs}
|
||||
rowData={localData}
|
||||
rowHeight={60}
|
||||
customThemeParams={
|
||||
theme === 'dark'
|
||||
? constants.agGridDarkVariables
|
||||
: constants.agGridLightVariables
|
||||
}
|
||||
onGridReady={handleOnGridReady}
|
||||
ref={gridRef}
|
||||
overlayNoRowsTemplate={t('No data to display')}
|
||||
suppressContextMenu
|
||||
getRowId={getRowId}
|
||||
suppressMovableColumns
|
||||
/>
|
||||
<div className="py-2">{`${t('settled in')} ${
|
||||
market.tradableInstrument.instrument.product
|
||||
.settlementAsset.symbol
|
||||
}`}</div>
|
||||
</div>
|
||||
<div className="w-full grid sm:grid-rows-2">
|
||||
<div>
|
||||
<SimpleMarketPercentChange
|
||||
candles={market.candles}
|
||||
marketId={market.id}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Lozenge
|
||||
variant={MARKET_STATUS[market.data?.market.state || '']}
|
||||
>
|
||||
{market.data?.market.state}
|
||||
</Lozenge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute right-16 top-1/2 -translate-y-1/2">
|
||||
<Button
|
||||
onClick={() => onClick(market.id)}
|
||||
variant="inline-link"
|
||||
prependIconName="chevron-right"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<Splash>{t('No data to display')}</Splash>
|
||||
)}
|
||||
</AsyncRenderer>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -7,11 +7,16 @@ import type { SimpleMarkets_markets_candles } from './__generated__/SimpleMarket
|
||||
|
||||
describe('SimpleMarketPercentChange should parse proper change', () => {
|
||||
let candles: (SimpleMarkets_markets_candles | null)[] | null;
|
||||
const setValue = () => undefined;
|
||||
it('empty array', () => {
|
||||
candles = [];
|
||||
render(
|
||||
<MockedProvider>
|
||||
<SimpleMarketPercentChange candles={candles} marketId={'1'} />
|
||||
<SimpleMarketPercentChange
|
||||
candles={candles}
|
||||
marketId={'1'}
|
||||
setValue={setValue}
|
||||
/>
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
@ -20,7 +25,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
|
||||
candles = null;
|
||||
render(
|
||||
<MockedProvider>
|
||||
<SimpleMarketPercentChange candles={candles} marketId={'1'} />
|
||||
<SimpleMarketPercentChange
|
||||
candles={candles}
|
||||
marketId={'1'}
|
||||
setValue={setValue}
|
||||
/>
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
@ -33,7 +42,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
|
||||
];
|
||||
render(
|
||||
<MockedProvider>
|
||||
<SimpleMarketPercentChange candles={candles} marketId={'1'} />
|
||||
<SimpleMarketPercentChange
|
||||
candles={candles}
|
||||
marketId={'1'}
|
||||
setValue={setValue}
|
||||
/>
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(screen.getByText('100.000%')).toBeInTheDocument();
|
||||
@ -49,7 +62,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
|
||||
];
|
||||
render(
|
||||
<MockedProvider>
|
||||
<SimpleMarketPercentChange candles={candles} marketId={'1'} />
|
||||
<SimpleMarketPercentChange
|
||||
candles={candles}
|
||||
marketId={'1'}
|
||||
setValue={setValue}
|
||||
/>
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(screen.getByText('-50.000%')).toBeInTheDocument();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { InView } from 'react-intersection-observer';
|
||||
import { useSubscription } from '@apollo/client';
|
||||
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
|
||||
@ -12,6 +12,7 @@ import { CANDLE_SUB } from './data-provider';
|
||||
interface Props {
|
||||
candles: (SimpleMarkets_markets_candles | null)[] | null;
|
||||
marketId: string;
|
||||
setValue: (arg: unknown) => void;
|
||||
}
|
||||
|
||||
const getChange = (
|
||||
@ -51,20 +52,32 @@ const SimpleMarketPercentChangeWrapper = (props: Props) => {
|
||||
|
||||
return (
|
||||
// @ts-ignore falsy wrong type?
|
||||
<InView onChange={setInView}>
|
||||
<InView
|
||||
onChange={setInView}
|
||||
className="flex h-full items-center justify-center"
|
||||
>
|
||||
{inView && <SimpleMarketPercentChange {...props} />}
|
||||
</InView>
|
||||
);
|
||||
};
|
||||
|
||||
const SimpleMarketPercentChange = ({ candles, marketId }: Props) => {
|
||||
const SimpleMarketPercentChange = ({ candles, marketId, setValue }: Props) => {
|
||||
const { data: { candles: { close = undefined } = {} } = {} } =
|
||||
useSubscription<CandleLive, CandleLiveVariables>(CANDLE_SUB, {
|
||||
variables: { marketId },
|
||||
});
|
||||
const change = getChange(candles, close);
|
||||
const color = getColor(change);
|
||||
return <p style={{ color }}>{change}</p>;
|
||||
useEffect(() => {
|
||||
const value = parseFloat(change);
|
||||
setValue(isNaN(value) ? '-' : value);
|
||||
}, [setValue, change]);
|
||||
|
||||
return (
|
||||
<div className="flex text-center" style={{ color }}>
|
||||
{change}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SimpleMarketPercentChangeWrapper;
|
||||
|
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
|
||||
import SimpleMarketExpires from './simple-market-expires';
|
||||
|
||||
interface Props {
|
||||
data: SimpleMarkets_markets;
|
||||
}
|
||||
|
||||
const MarketNameRenderer = ({ data }: Props) => {
|
||||
return (
|
||||
<div className="flex h-full items-center grid grid-rows-2 grid-flow-col gap-x-8 gap-y-0 grid-cols-[min-content,1fr,1fr]">
|
||||
<div className="w-60 row-span-2 bg-pink rounded-full w-44 h-44 bg-gradient-to-br from-white-60 to--white-80 opacity-30" />
|
||||
<div className="col-span-2 uppercase justify-start text-black dark:text-white text-market self-end">
|
||||
{data.name}{' '}
|
||||
<SimpleMarketExpires
|
||||
tags={data.tradableInstrument.instrument.metadata.tags}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-2 ui-small text-deemphasise dark:text-midGrey self-start leading-3">
|
||||
{data.tradableInstrument.instrument.product.quoteName}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarketNameRenderer;
|
@ -67,7 +67,7 @@ const SimpleMarketToolbar = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="w-max mb-16 font-alpha">
|
||||
<div className="w-max mb-32 font-alpha">
|
||||
<ul
|
||||
ref={slideContRef}
|
||||
className="grid grid-flow-col auto-cols-min gap-8 relative pb-4 mb-16"
|
||||
@ -162,7 +162,7 @@ const SimpleMarketToolbar = () => {
|
||||
</div>
|
||||
{activeNumber > 0 && (
|
||||
<ul
|
||||
className="grid grid-flow-col auto-cols-min md:gap-16 sm:gap-12 pb-4 md:ml-16"
|
||||
className="grid grid-flow-col auto-cols-min md:gap-16 gap-12 pb-4 md:ml-16"
|
||||
data-testid="market-assets-menu"
|
||||
aria-label={t('Asset on the market')}
|
||||
>
|
||||
|
125
apps/simple-trading-app/src/app/hooks/use-column-definitions.tsx
Normal file
125
apps/simple-trading-app/src/app/hooks/use-column-definitions.tsx
Normal file
@ -0,0 +1,125 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import type { SimpleMarkets_markets } from '../components/simple-market-list/__generated__/SimpleMarkets';
|
||||
import MarketNameRenderer from '../components/simple-market-list/simple-market-renderer';
|
||||
import SimpleMarketPercentChange from '../components/simple-market-list/simple-market-percent-change';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ValueSetterParams } from 'ag-grid-community';
|
||||
import type { SimpleMarketsType } from '../components/simple-market-list/simple-market-list';
|
||||
|
||||
interface Props {
|
||||
onClick: (marketId: string) => void;
|
||||
}
|
||||
|
||||
const useColumnDefinitions = ({ onClick }: Props) => {
|
||||
const columnDefs = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
colId: 'market',
|
||||
headerName: t('Markets'),
|
||||
headerClass: 'uppercase',
|
||||
minWidth: 300,
|
||||
field: 'name',
|
||||
cellRenderer: ({ data }: { data: SimpleMarketsType }) => (
|
||||
<MarketNameRenderer data={data} />
|
||||
),
|
||||
},
|
||||
{
|
||||
colId: 'asset',
|
||||
headerName: t('Settlement asset'),
|
||||
headerClass: 'uppercase',
|
||||
minWidth: 100,
|
||||
cellClass: 'uppercase flex h-full items-center',
|
||||
field: 'tradableInstrument.instrument.product.settlementAsset.symbol',
|
||||
cellRenderer: ({ data }: { data: SimpleMarketsType }) => (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
{data.tradableInstrument.instrument.product.settlementAsset.symbol}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
colId: 'change',
|
||||
headerName: t('24h change'),
|
||||
headerClass: 'uppercase',
|
||||
field: 'percentChange',
|
||||
minWidth: 100,
|
||||
valueSetter: (params: ValueSetterParams): boolean => {
|
||||
const { oldValue, newValue, api, data } = params;
|
||||
if (oldValue !== newValue) {
|
||||
const newdata = { percentChange: newValue, ...data };
|
||||
api.applyTransaction({ update: [newdata] });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
cellRenderer: ({
|
||||
data,
|
||||
setValue,
|
||||
}: {
|
||||
data: SimpleMarketsType;
|
||||
setValue: (arg: unknown) => void;
|
||||
}) => (
|
||||
<SimpleMarketPercentChange
|
||||
candles={data.candles}
|
||||
marketId={data.id}
|
||||
setValue={setValue}
|
||||
/>
|
||||
),
|
||||
comparator: (valueA: number | '-', valueB: number | '-') => {
|
||||
if (valueA === valueB) return 0;
|
||||
if (valueA === '-') {
|
||||
return -1;
|
||||
}
|
||||
if (valueB === '-') {
|
||||
return 1;
|
||||
}
|
||||
return valueA > valueB ? 1 : -1;
|
||||
},
|
||||
},
|
||||
{
|
||||
colId: 'status',
|
||||
headerName: t('Status'),
|
||||
field: 'data.market.state',
|
||||
headerClass: 'uppercase',
|
||||
minWidth: 100,
|
||||
cellRenderer: ({ data }: { data: SimpleMarkets_markets }) => (
|
||||
<div className="uppercase flex h-full items-center justify-center">
|
||||
<div className="border text-center px-8">
|
||||
{data.data?.market.state}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
colId: 'trade',
|
||||
headerName: '',
|
||||
headerClass: 'uppercase',
|
||||
sortable: false,
|
||||
minWidth: 100,
|
||||
cellRenderer: ({ data }: { data: SimpleMarkets_markets }) => (
|
||||
<div className="h-full flex h-full items-center justify-end">
|
||||
<Button
|
||||
onClick={() => onClick(data.id)}
|
||||
variant="inline-link"
|
||||
appendIconName="arrow-top-right"
|
||||
className="uppercase no-underline hover:no-underline"
|
||||
>
|
||||
{t('Trade')}
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
}, [onClick]);
|
||||
|
||||
const defaultColDef = useMemo(() => {
|
||||
return {
|
||||
sortable: true,
|
||||
unSortIcon: true,
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { columnDefs, defaultColDef };
|
||||
};
|
||||
|
||||
export default useColumnDefinitions;
|
@ -4,20 +4,12 @@ import { SimpleMarketList } from '../components/simple-market-list';
|
||||
import { Portfolio } from '../components/portfolio';
|
||||
|
||||
export const ROUTES = {
|
||||
HOME: '/',
|
||||
MARKETS: 'markets',
|
||||
TRADING: 'trading',
|
||||
LIQUIDITY: 'liquidity',
|
||||
PORTFOLIO: 'portfolio',
|
||||
};
|
||||
|
||||
export const routerConfig = [
|
||||
{
|
||||
path: ROUTES.HOME,
|
||||
name: 'Home',
|
||||
text: t('Home'),
|
||||
element: <div>Home</div>,
|
||||
},
|
||||
{
|
||||
path: ROUTES.MARKETS,
|
||||
children: [
|
||||
@ -36,6 +28,7 @@ export const routerConfig = [
|
||||
name: 'Markets',
|
||||
text: t('Markets'),
|
||||
element: <SimpleMarketList />,
|
||||
icon: 'market',
|
||||
},
|
||||
{
|
||||
path: ROUTES.TRADING,
|
||||
@ -48,17 +41,13 @@ export const routerConfig = [
|
||||
element: <DealTicketContainer />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: ROUTES.LIQUIDITY,
|
||||
name: 'Liquidity',
|
||||
text: t('Liquidity'),
|
||||
element: <div>Liquidity</div>,
|
||||
icon: 'trade',
|
||||
},
|
||||
{
|
||||
path: ROUTES.PORTFOLIO,
|
||||
name: 'Portfolio',
|
||||
text: t('Portfolio'),
|
||||
element: <Portfolio />,
|
||||
icon: 'portfolio',
|
||||
},
|
||||
];
|
||||
|
@ -38,7 +38,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "129999.45",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "124661.013711767544930765",
|
||||
"locked_amount": "124186.56041865939631899",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129999.45",
|
||||
@ -488,7 +488,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "97499.58",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "65503.04337545080622283",
|
||||
"locked_amount": "65037.648892235152359708",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "97499.58",
|
||||
@ -521,7 +521,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "135173.4239508",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "89531.35166557213976323285608",
|
||||
"locked_amount": "88895.23775402196371772604572",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "135173.4239508",
|
||||
@ -554,7 +554,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "32499.86",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "27555.99917099787937361",
|
||||
"locked_amount": "27360.21574884189648315",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "32499.86",
|
||||
@ -587,7 +587,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "10833.29",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "8969.210333073256934278",
|
||||
"locked_amount": "8905.484728998842512346",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10833.29",
|
||||
@ -675,7 +675,7 @@
|
||||
"tranche_end": "2022-11-01T00:00:00.000Z",
|
||||
"total_added": "22500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "15468.3027626811615",
|
||||
"locked_amount": "15223.73612998188375",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "15000",
|
||||
@ -761,7 +761,7 @@
|
||||
"tranche_end": "2023-06-02T00:00:00.000Z",
|
||||
"total_added": "1939928.38",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "1804379.69836047439128314",
|
||||
"locked_amount": "1793749.89229780380096827",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1852091.69",
|
||||
@ -1777,7 +1777,7 @@
|
||||
"tranche_end": "2022-09-30T00:00:00.000Z",
|
||||
"total_added": "60916.66666633337",
|
||||
"total_removed": "18705.279504739679372649",
|
||||
"locked_amount": "14760.0056484630890178248654459722",
|
||||
"locked_amount": "14447.6106782408306155580286543045",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2833.333333",
|
||||
@ -5226,9 +5226,9 @@
|
||||
"tranche_id": 11,
|
||||
"tranche_start": "2021-09-03T00:00:00.000Z",
|
||||
"tranche_end": "2022-09-03T00:00:00.000Z",
|
||||
"total_added": "19455.000000000000000003",
|
||||
"total_added": "19457.000000000000000003",
|
||||
"total_removed": "5056.88782409978",
|
||||
"locked_amount": "3597.64752092846370375055476445966514475",
|
||||
"locked_amount": "3491.40304829401407518053832600837138522",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "75",
|
||||
@ -5260,6 +5260,11 @@
|
||||
"user": "0x3a380f7CFdEeb723228cA57d2795EA215094000d",
|
||||
"tx": "0x72adba1aa2d918148f1a95eb6a447da22fa06217cadafa94d56f12fd22a389be"
|
||||
},
|
||||
{
|
||||
"amount": "2",
|
||||
"user": "0xB2BddeEb907797F9be51E3f9f75824f83cD71B67",
|
||||
"tx": "0x09792a45790c762f046f34979cbbe7687661ce7643da9ba6bc9c128710191b46"
|
||||
},
|
||||
{
|
||||
"amount": "30",
|
||||
"user": "0xb4eE687f019A8e48F7087f4b4f8653208B8cc48f",
|
||||
@ -8669,6 +8674,21 @@
|
||||
"withdrawn_tokens": "940.30940496195",
|
||||
"remaining_tokens": "649.69059503805"
|
||||
},
|
||||
{
|
||||
"address": "0xB2BddeEb907797F9be51E3f9f75824f83cD71B67",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2",
|
||||
"user": "0xB2BddeEb907797F9be51E3f9f75824f83cD71B67",
|
||||
"tranche_id": 11,
|
||||
"tx": "0x09792a45790c762f046f34979cbbe7687661ce7643da9ba6bc9c128710191b46"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "2",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "2"
|
||||
},
|
||||
{
|
||||
"address": "0xb4eE687f019A8e48F7087f4b4f8653208B8cc48f",
|
||||
"deposits": [
|
||||
@ -14123,7 +14143,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "3732368.4671",
|
||||
"total_removed": "74162.9780761646031",
|
||||
"locked_amount": "2797204.7028220379638062054",
|
||||
"locked_amount": "2780870.39180910746474557688",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1998.95815",
|
||||
@ -14835,8 +14855,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "15788853.065470999700000001",
|
||||
"total_removed": "7926.7627792759659",
|
||||
"locked_amount": "15140482.7365636033722263648641426217383077",
|
||||
"total_removed": "8648.631845187181275",
|
||||
"locked_amount": "15082858.8517601414887782644731138155812382",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "16249.93",
|
||||
@ -15360,6 +15380,21 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xa79f7f3e6436a1f473f3beab9e0a5c8bc4f52b38ac7aedb8610a1a9a9c4a786c"
|
||||
},
|
||||
{
|
||||
"amount": "180.278846094717375",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xca332ec8365253d3b17445c182d287e56783ab4552f3581f3a43dc6334fbfc7b"
|
||||
},
|
||||
{
|
||||
"amount": "173.18268014001675",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x793dabbba22d3e8e88f7600162ee6456d6d1b58e49ce07209d3f97eaa485161f"
|
||||
},
|
||||
{
|
||||
"amount": "368.40753967648125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x959378e42a224a7cff0dde794e80a0f07cbbe6b61e9158092a037efaa726aa28"
|
||||
},
|
||||
{
|
||||
"amount": "2446.31552516990115",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@ -15482,6 +15517,24 @@
|
||||
"tranche_id": 2,
|
||||
"tx": "0xa79f7f3e6436a1f473f3beab9e0a5c8bc4f52b38ac7aedb8610a1a9a9c4a786c"
|
||||
},
|
||||
{
|
||||
"amount": "180.278846094717375",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0xca332ec8365253d3b17445c182d287e56783ab4552f3581f3a43dc6334fbfc7b"
|
||||
},
|
||||
{
|
||||
"amount": "173.18268014001675",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x793dabbba22d3e8e88f7600162ee6456d6d1b58e49ce07209d3f97eaa485161f"
|
||||
},
|
||||
{
|
||||
"amount": "368.40753967648125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x959378e42a224a7cff0dde794e80a0f07cbbe6b61e9158092a037efaa726aa28"
|
||||
},
|
||||
{
|
||||
"amount": "2446.31552516990115",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@ -15526,8 +15579,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "194999.1675",
|
||||
"withdrawn_tokens": "7926.7627792759659",
|
||||
"remaining_tokens": "187072.4047207240341"
|
||||
"withdrawn_tokens": "8648.631845187181275",
|
||||
"remaining_tokens": "186350.535654812818725"
|
||||
},
|
||||
{
|
||||
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
|
||||
@ -16931,8 +16984,8 @@
|
||||
"tranche_start": "2021-11-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-05T00:00:00.000Z",
|
||||
"total_added": "14597706.0446472999",
|
||||
"total_removed": "2113641.840890679071831632",
|
||||
"locked_amount": "8328080.66510881884167481291924447",
|
||||
"total_removed": "2114974.470447369009412382",
|
||||
"locked_amount": "8274608.90495455977196820197238862",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129284.449",
|
||||
@ -17181,6 +17234,21 @@
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0xc62ffa6bf5029422f44d9406972fc074b498e02f667a86ae9faba138b6cfd758"
|
||||
},
|
||||
{
|
||||
"amount": "331.7071722886032605",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0xc279c1a171709ff76a9875fea2e8f98ceb5797714ead511ba95a12f61412fc13"
|
||||
},
|
||||
{
|
||||
"amount": "321.15600624174480075",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x7289f6af545a72235c26ecd98b8a0c14fbb5e62af98ded20862cfad2ef59d3bc"
|
||||
},
|
||||
{
|
||||
"amount": "679.7663781595895195",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x4cef72f21dd9cf51cb6fbc965bc0b514b9c2ea4e2d9ca62f19c5a5defc6eef60"
|
||||
},
|
||||
{
|
||||
"amount": "652.48254356494551875",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@ -18799,6 +18867,24 @@
|
||||
"tranche_id": 3,
|
||||
"tx": "0xc62ffa6bf5029422f44d9406972fc074b498e02f667a86ae9faba138b6cfd758"
|
||||
},
|
||||
{
|
||||
"amount": "331.7071722886032605",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0xc279c1a171709ff76a9875fea2e8f98ceb5797714ead511ba95a12f61412fc13"
|
||||
},
|
||||
{
|
||||
"amount": "321.15600624174480075",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x7289f6af545a72235c26ecd98b8a0c14fbb5e62af98ded20862cfad2ef59d3bc"
|
||||
},
|
||||
{
|
||||
"amount": "679.7663781595895195",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x4cef72f21dd9cf51cb6fbc965bc0b514b9c2ea4e2d9ca62f19c5a5defc6eef60"
|
||||
},
|
||||
{
|
||||
"amount": "652.48254356494551875",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@ -20037,8 +20123,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "359123.469575",
|
||||
"withdrawn_tokens": "154092.9757912160630365",
|
||||
"remaining_tokens": "205030.4937837839369635"
|
||||
"withdrawn_tokens": "155425.60534790600061725",
|
||||
"remaining_tokens": "203697.86422709399938275"
|
||||
},
|
||||
{
|
||||
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
|
||||
@ -21034,8 +21120,8 @@
|
||||
"tranche_start": "2021-10-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "5778205.3912159303",
|
||||
"total_removed": "1390546.591547348229906227",
|
||||
"locked_amount": "2973571.63505682836710488797665665",
|
||||
"total_removed": "1404291.506600519461757227",
|
||||
"locked_amount": "2952444.61922536743378942253962054",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "552496.6455",
|
||||
@ -21184,6 +21270,16 @@
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
"tx": "0xa6b9993b9288eac739756499b2997155ce8b1bc6e862c1a6569c8e04463bb31f"
|
||||
},
|
||||
{
|
||||
"amount": "3635.564694620199747",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
"tx": "0x89b7e9f6200ba171c5f1115f9fd110284ef4ffaba58cf174b686bbe4905fe18a"
|
||||
},
|
||||
{
|
||||
"amount": "10109.350358551032104",
|
||||
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
|
||||
"tx": "0xc695996be3df837d7040aec8c5532965ac6554d5615575ee46fc87d604e2ebc0"
|
||||
},
|
||||
{
|
||||
"amount": "13341.31568777778021",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
@ -21666,6 +21762,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "10109.350358551032104",
|
||||
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
|
||||
"tranche_id": 4,
|
||||
"tx": "0xc695996be3df837d7040aec8c5532965ac6554d5615575ee46fc87d604e2ebc0"
|
||||
},
|
||||
{
|
||||
"amount": "7013.412182841867109",
|
||||
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
|
||||
@ -21722,8 +21824,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "110499.5291",
|
||||
"withdrawn_tokens": "43654.936134887817154",
|
||||
"remaining_tokens": "66844.592965112182846"
|
||||
"withdrawn_tokens": "53764.286493438849258",
|
||||
"remaining_tokens": "56735.242606561150742"
|
||||
},
|
||||
{
|
||||
"address": "0xdbC5d439F373EB646345e1c67D1d46231ACE7dD3",
|
||||
@ -22018,6 +22120,12 @@
|
||||
"tranche_id": 4,
|
||||
"tx": "0xa6b9993b9288eac739756499b2997155ce8b1bc6e862c1a6569c8e04463bb31f"
|
||||
},
|
||||
{
|
||||
"amount": "3635.564694620199747",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
"tranche_id": 4,
|
||||
"tx": "0x89b7e9f6200ba171c5f1115f9fd110284ef4ffaba58cf174b686bbe4905fe18a"
|
||||
},
|
||||
{
|
||||
"amount": "13341.31568777778021",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
@ -22080,8 +22188,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "331498.5873",
|
||||
"withdrawn_tokens": "157304.28913691490831",
|
||||
"remaining_tokens": "174194.29816308509169"
|
||||
"withdrawn_tokens": "160939.853831535108057",
|
||||
"remaining_tokens": "170558.733468464891943"
|
||||
},
|
||||
{
|
||||
"address": "0x16da609341ed67750A8BCC5AAa2005471006Cd77",
|
||||
@ -22158,7 +22266,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "472355.6199999996",
|
||||
"total_removed": "34.173053016",
|
||||
"locked_amount": "443233.07466590775804275636428212",
|
||||
"locked_amount": "440644.80971500656998341987011672",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "3000",
|
||||
@ -47836,7 +47944,7 @@
|
||||
"tranche_start": "2021-12-05T00:00:00.000Z",
|
||||
"tranche_end": "2022-06-05T00:00:00.000Z",
|
||||
"total_added": "171288.42",
|
||||
"total_removed": "32094.1716569431377",
|
||||
"total_removed": "32508.6592656906377",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@ -52136,6 +52244,21 @@
|
||||
"user": "0xDD5730a33719083470e641cF0e4154Dd04D5738d",
|
||||
"tx": "0x49bd6332008e65069aad8012f76f15f3dae19f664237b02f9152946297db812d"
|
||||
},
|
||||
{
|
||||
"amount": "101.100697625",
|
||||
"user": "0x6886CA9FfE30C5BB86590c71799Bf5550EDF845b",
|
||||
"tx": "0x204f08eb22345e352efcbfd3921f813fdf1097ea3fc33b36387c3c1cc5678936"
|
||||
},
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0xbCc28D073f7a96c0568D36FdB21A98105ee60Ca3",
|
||||
"tx": "0xf1201851e334cac5ab0920f89874163c5074c608b8d810b5eb89fb62454ddc19"
|
||||
},
|
||||
{
|
||||
"amount": "63.3869111225",
|
||||
"user": "0x002dA530f8691f012Cecf0ba859Dd5dFa7d9871D",
|
||||
"tx": "0x901a76735076a5058146ce6fc79628d78d71e8d2b3161459c2daf64ae043f1d5"
|
||||
},
|
||||
{
|
||||
"amount": "183.6335597275",
|
||||
"user": "0x690Fc36d52eD3f198F0eBDea1557333a1766f786",
|
||||
@ -53517,6 +53640,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "101.100697625",
|
||||
"user": "0x6886CA9FfE30C5BB86590c71799Bf5550EDF845b",
|
||||
"tranche_id": 6,
|
||||
"tx": "0x204f08eb22345e352efcbfd3921f813fdf1097ea3fc33b36387c3c1cc5678936"
|
||||
},
|
||||
{
|
||||
"amount": "148.899302375",
|
||||
"user": "0x6886CA9FfE30C5BB86590c71799Bf5550EDF845b",
|
||||
@ -53525,8 +53654,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "250",
|
||||
"withdrawn_tokens": "148.899302375",
|
||||
"remaining_tokens": "101.100697625"
|
||||
"withdrawn_tokens": "250",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0xDFdC0c291ee8499777272ab3A7B786518c895966",
|
||||
@ -56458,6 +56587,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "63.3869111225",
|
||||
"user": "0x002dA530f8691f012Cecf0ba859Dd5dFa7d9871D",
|
||||
"tranche_id": 6,
|
||||
"tx": "0x901a76735076a5058146ce6fc79628d78d71e8d2b3161459c2daf64ae043f1d5"
|
||||
},
|
||||
{
|
||||
"amount": "186.6130888775",
|
||||
"user": "0x002dA530f8691f012Cecf0ba859Dd5dFa7d9871D",
|
||||
@ -56466,8 +56601,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "250",
|
||||
"withdrawn_tokens": "186.6130888775",
|
||||
"remaining_tokens": "63.3869111225"
|
||||
"withdrawn_tokens": "250",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0xDE321bC0E688C3cfda2D07f4BE1B7D4fB019cc91",
|
||||
@ -65762,10 +65897,17 @@
|
||||
"tx": "0xc8541da6a57f410b6faba47a5e5184bae700193b7bd042914fffc562114d92f5"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0xbCc28D073f7a96c0568D36FdB21A98105ee60Ca3",
|
||||
"tranche_id": 6,
|
||||
"tx": "0xf1201851e334cac5ab0920f89874163c5074c608b8d810b5eb89fb62454ddc19"
|
||||
}
|
||||
],
|
||||
"total_tokens": "250",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "250"
|
||||
"withdrawn_tokens": "250",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x8c951C54F9cd08Cf81F770248e835D7A4F491a46",
|
||||
|
@ -38,7 +38,7 @@
|
||||
"tranche_end": "2022-11-26T13:48:10.000Z",
|
||||
"total_added": "100",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "41.663429096905125",
|
||||
"locked_amount": "41.115480720446473",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "100",
|
||||
@ -242,7 +242,7 @@
|
||||
"tranche_end": "2022-10-12T00:53:20.000Z",
|
||||
"total_added": "1100",
|
||||
"total_removed": "673.04388635",
|
||||
"locked_amount": "321.05967465753423",
|
||||
"locked_amount": "315.032242516489036",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1000",
|
||||
|
@ -69,7 +69,7 @@
|
||||
"tranche_end": "2022-10-12T00:53:20.000Z",
|
||||
"total_added": "1010.000000000000000001",
|
||||
"total_removed": "668.4622323651",
|
||||
"locked_amount": "294.7911558219177930002918724315068493",
|
||||
"locked_amount": "289.25687721968538760028639294774226276",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1000",
|
||||
|
18
apps/stats-e2e/cypress.config.js
Normal file
18
apps/stats-e2e/cypress.config.js
Normal file
@ -0,0 +1,18 @@
|
||||
const { defineConfig } = require('cypress');
|
||||
module.exports = defineConfig({
|
||||
projectId: 'et4snf',
|
||||
|
||||
e2e: {
|
||||
baseUrl: 'http://localhost:3010',
|
||||
fileServerFolder: '.',
|
||||
fixturesFolder: false,
|
||||
specPattern: './src/integration/*.ts',
|
||||
excludeSpecPattern: '**/*.js',
|
||||
modifyObstructiveCode: false,
|
||||
supportFile: './src/support/index.ts',
|
||||
video: true,
|
||||
videosFolder: '../../dist/cypress/apps/explorer-e2e/videos',
|
||||
screenshotsFolder: '../../dist/cypress/apps/explorer-e2e/screenshots',
|
||||
chromeWebSecurity: false,
|
||||
},
|
||||
});
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:3010",
|
||||
"projectId": "et4snf",
|
||||
"fileServerFolder": ".",
|
||||
"fixturesFolder": "./src/fixtures",
|
||||
"integrationFolder": "./src/integration",
|
||||
"modifyObstructiveCode": false,
|
||||
"supportFile": "./src/support/index.ts",
|
||||
"pluginsFile": false,
|
||||
"video": true,
|
||||
"videosFolder": "../../dist/cypress/apps/stats-e2e/videos",
|
||||
"screenshotsFolder": "../../dist/cypress/apps/stats-e2e/screenshots",
|
||||
"chromeWebSecurity": false
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
"e2e": {
|
||||
"executor": "@nrwl/cypress:cypress",
|
||||
"options": {
|
||||
"cypressConfig": "apps/stats-e2e/cypress.json",
|
||||
"cypressConfig": "apps/stats-e2e/cypress.config.js",
|
||||
"devServerTarget": "stats:serve"
|
||||
},
|
||||
"configurations": {
|
||||
|
17
apps/token-e2e/cypress.config.js
Normal file
17
apps/token-e2e/cypress.config.js
Normal file
@ -0,0 +1,17 @@
|
||||
const { defineConfig } = require('cypress');
|
||||
module.exports = defineConfig({
|
||||
projectId: 'et4snf',
|
||||
|
||||
e2e: {
|
||||
baseUrl: 'http://localhost:4210',
|
||||
fileServerFolder: '.',
|
||||
fixturesFolder: false,
|
||||
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
|
||||
modifyObstructiveCode: false,
|
||||
supportFile: './src/support/index.ts',
|
||||
video: true,
|
||||
videosFolder: '../../dist/cypress/apps/explorer-e2e/videos',
|
||||
screenshotsFolder: '../../dist/cypress/apps/explorer-e2e/screenshots',
|
||||
chromeWebSecurity: false,
|
||||
},
|
||||
});
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:4210",
|
||||
"projectId": "et4snf",
|
||||
"fileServerFolder": ".",
|
||||
"fixturesFolder": "./src/fixtures",
|
||||
"integrationFolder": "./src/integration",
|
||||
"modifyObstructiveCode": false,
|
||||
"supportFile": "./src/support/index.ts",
|
||||
"pluginsFile": false,
|
||||
"video": true,
|
||||
"videosFolder": "../../dist/cypress/apps/token-e2e/videos",
|
||||
"screenshotsFolder": "../../dist/cypress/apps/token-e2e/screenshots",
|
||||
"chromeWebSecurity": false
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
"e2e": {
|
||||
"executor": "@nrwl/cypress:cypress",
|
||||
"options": {
|
||||
"cypressConfig": "apps/token-e2e/cypress.json",
|
||||
"cypressConfig": "apps/token-e2e/cypress.config.js",
|
||||
"devServerTarget": "token:serve"
|
||||
},
|
||||
"configurations": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import navigation from '../../locators/navigation.locators';
|
||||
import home from '../../locators/home.locators';
|
||||
import vegaToken from '../../data/vegaToken.json';
|
||||
import navigation from '../locators/navigation.locators';
|
||||
import home from '../locators/home.locators';
|
||||
import vegaToken from '../data/vegaToken.json';
|
||||
|
||||
context('Home Page - verify elements on page', function () {
|
||||
before('visit token home page', function () {
|
@ -7,7 +7,7 @@ import { ENV } from '../../config/env';
|
||||
export const AppFooter = () => {
|
||||
return (
|
||||
<footer className="p-12 text-ui">
|
||||
<p className="mb-8">Version: {ENV.commit || 'development'}</p>
|
||||
<p>Version: {ENV.commit || 'development'}</p>
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="footerLinksText"
|
||||
|
@ -9,7 +9,10 @@ interface BulletHeaderProps {
|
||||
export const BulletHeader = ({ tag, children, style }: BulletHeaderProps) => {
|
||||
return React.createElement(
|
||||
tag,
|
||||
{ className: 'mt-24 pt-8 pb-20 uppercase text-white', style },
|
||||
{
|
||||
className: 'mt-24 py-8 border-t border-white uppercase text-white',
|
||||
style,
|
||||
},
|
||||
<>
|
||||
<span className="inline-block w-[12px] h-[12px] mr-12 bg-white" />
|
||||
{children}
|
||||
|
@ -9,9 +9,7 @@ export const ConnectedVegaKey = ({ pubKey }: { pubKey: string | null }) => {
|
||||
<strong data-testid="connected-vega-key-label">
|
||||
{pubKey ? t('Connected Vega key') : <ConnectToVega />}
|
||||
</strong>
|
||||
<p className="mb-12" data-testid="connected-vega-key">
|
||||
{pubKey}
|
||||
</p>
|
||||
<p data-testid="connected-vega-key">{pubKey}</p>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
@ -74,19 +74,24 @@ const AssociatedAmounts = ({
|
||||
light={false}
|
||||
/>
|
||||
{vestingAssociationByVegaKey.length ? (
|
||||
<>
|
||||
<hr style={{ borderStyle: 'dashed', color: Colors.text }} />
|
||||
<WalletCardRow label="Associated with Vega keys" bold={true} />
|
||||
<div>
|
||||
<hr style={{ borderStyle: 'dashed' }} />
|
||||
<WalletCardRow
|
||||
label="Associated with Vega keys"
|
||||
bold={true}
|
||||
dark={true}
|
||||
/>
|
||||
{vestingAssociationByVegaKey.map(([key, amount]) => {
|
||||
return (
|
||||
<WalletCardRow
|
||||
key={key}
|
||||
label={removeLeadingAddressSymbol(key)}
|
||||
value={amount}
|
||||
dark={true}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
@ -192,9 +197,9 @@ export const EthWallet = () => {
|
||||
return (
|
||||
<WalletCard dark={true}>
|
||||
<WalletCardHeader>
|
||||
<h1 className="text-h3 uppercase">{t('ethereumKey')}</h1>
|
||||
<h1 className="m-0">{t('ethereumKey')}</h1>
|
||||
{account && (
|
||||
<div className="px-4 text-right">
|
||||
<div className="place-self-end font-mono px-4 pb-2">
|
||||
<div className="font-mono">{truncateMiddle(account)}</div>
|
||||
{pendingTxs && (
|
||||
<div>
|
||||
@ -222,7 +227,6 @@ export const EthWallet = () => {
|
||||
) : (
|
||||
<Button
|
||||
variant={'secondary'}
|
||||
className="w-full px-28 border h-28"
|
||||
onClick={() =>
|
||||
appDispatch({
|
||||
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
|
||||
|
@ -7,9 +7,7 @@ export const Heading = ({ title }: HeadingProps) => {
|
||||
|
||||
return (
|
||||
<header className="my-0 mx-auto">
|
||||
<h1 className="font-alpha calt text-h3 text-white uppercase mb-4">
|
||||
{title}
|
||||
</h1>
|
||||
<h1 className="font-alpha calt">{title}</h1>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
@ -1 +1 @@
|
||||
export { Heading } from './heading';
|
||||
export * from './heading';
|
||||
|
@ -14,7 +14,7 @@ const ProgressContents = ({
|
||||
children: React.ReactNode;
|
||||
}) => (
|
||||
<div
|
||||
className={classnames('flex justify-between py-2 font-mono', {
|
||||
className={classnames('flex justify-between py-2 font-mono mb-2', {
|
||||
'gap-0 px-0 text-black': light,
|
||||
'gap-y-0 gap-x-4': !light,
|
||||
})}
|
||||
@ -93,7 +93,7 @@ export const LockedProgress = ({
|
||||
}, [total, unlocked]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-8">
|
||||
<div
|
||||
className={classnames('flex border', {
|
||||
'border-black': light,
|
||||
@ -122,6 +122,6 @@ export const LockedProgress = ({
|
||||
<span>{formatNumber(locked, decimals)}</span>
|
||||
<span>{formatNumber(unlocked, decimals)}</span>
|
||||
</ProgressContents>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -5,7 +5,8 @@ import debounce from 'lodash/debounce';
|
||||
import React from 'react';
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { Link, NavLink } from 'react-router-dom';
|
||||
import vegaWhite from '../../images/vega_white.png';
|
||||
|
||||
import { Flags } from '../../config';
|
||||
import {
|
||||
@ -16,6 +17,69 @@ import { Routes } from '../../routes/router-config';
|
||||
import { EthWallet } from '../eth-wallet';
|
||||
import { VegaWallet } from '../vega-wallet';
|
||||
|
||||
const Fish = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="33"
|
||||
height="20"
|
||||
viewBox="0 0 200 116"
|
||||
fill="none"
|
||||
data-testid="fairground-icon"
|
||||
>
|
||||
<g clipPath="url(#clip0)">
|
||||
<path
|
||||
d="M70.5918 32.8569L70.5918 22.7932L60.5254 22.7932L60.5254 32.8569L70.5918 32.8569Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path
|
||||
d="M80.6641 83.2006L80.6641 73.1377L70.5977 73.1377L70.5977 83.2006L80.6641 83.2006Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path
|
||||
d="M70.5918 93.2409L70.5918 83.1772L60.5254 83.1772L60.5254 93.2409L70.5918 93.2409Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path
|
||||
d="M100.797 93.2636L100.797 73.1377L90.7305 73.1377L90.7305 93.2636L100.797 93.2636Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path
|
||||
d="M90.7285 103.33L90.7285 93.2671L80.662 93.2671L80.662 103.33L90.7285 103.33Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path
|
||||
d="M90.7285 22.8026L90.7285 12.74L80.662 12.74L80.662 22.8026L90.7285 22.8026Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path
|
||||
d="M110.869 12.6108L110.869 2.54785L100.803 2.54785L100.803 12.6108L110.869 12.6108Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path
|
||||
d="M120.934 103.326L120.934 73.1377L110.867 73.1377L110.867 103.326L120.934 103.326Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path
|
||||
d="M110.869 113.384L110.869 103.321L100.803 103.321L100.803 113.384L110.869 113.384Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path
|
||||
d="M161.328 52.9855L161.328 42.9226L151.262 42.9226L151.262 52.9855L161.328 52.9855Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path
|
||||
d="M20.133 83.187L30.3354 83.187L30.3354 73.124L40.4017 73.124L40.4017 63.0613L50.4681 63.0613L50.4681 73.124L60.5345 73.124L60.5345 63.0613L70.6008 63.0613L80.6672 63.0613L131.135 63.0613L131.135 113.376L161.334 113.376L161.334 103.313L171.4 103.313L171.4 93.25L181.467 93.25L181.467 83.187L191.533 83.187L191.533 63.0613L181.467 63.0613L181.467 73.1241L171.4 73.1241L171.4 83.187L161.334 83.187L161.334 73.1241L171.4 73.1241L171.4 63.0613L161.334 63.0613L151.268 63.0613L141.201 63.0613L141.201 52.9983L141.201 32.8726L161.334 32.8726L171.4 32.8726L171.4 63.0613L181.467 63.0613L181.467 52.9983L191.533 52.9983L191.533 32.8726L181.467 32.8726L181.467 22.8096L171.4 22.8096L171.4 12.7467L161.334 12.7467L161.334 2.54785L141.201 2.54785L131.135 2.54785L131.135 52.9983L120.933 52.9983L120.933 12.7467L110.866 12.7467L110.866 52.9983L100.8 52.9983L100.8 22.8096L90.7336 22.8096L90.7336 52.9983L80.6672 52.9983L80.6672 32.8726L70.6008 32.8726L70.6008 52.9983L60.5345 52.9983L60.5345 42.9354L50.4681 42.9354L50.4681 52.9983L40.4017 52.9983L40.4017 42.9354L30.3354 42.9354L30.3354 32.8726L20.133 32.8726L20.133 22.8096L0.000308081 22.8096L0.000307201 42.9354L10.0666 42.9354L10.0666 52.9983L20.133 52.9983L20.133 63.0613L10.0666 63.0613L10.0666 73.124L0.000305881 73.124L0.000305002 93.25L20.133 93.25L20.133 83.187Z"
|
||||
fill="black"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="200" height="116" fill="none" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const Nav = () => {
|
||||
const [windowWidth, setWindowWidth] = React.useState(window.innerWidth);
|
||||
const isDesktop = windowWidth > 959;
|
||||
@ -35,7 +99,7 @@ export const Nav = () => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`p-16 ${
|
||||
className={`px-16 py-8 ${
|
||||
inverted
|
||||
? 'bg-clouds bg-no-repeat bg-cover bg-vega-yellow'
|
||||
: 'border-b-white border-b-1'
|
||||
@ -46,7 +110,7 @@ export const Nav = () => {
|
||||
{!isDesktop && <NavHeader fairground={inverted} />}
|
||||
<div className="flex gap-12 lg:flex-auto">
|
||||
{isDesktop ? (
|
||||
<NavLinks isDesktop={isDesktop} />
|
||||
<NavLinks isDesktop={isDesktop} isInverted={inverted} />
|
||||
) : (
|
||||
<NavDrawer inverted={inverted} />
|
||||
)}
|
||||
@ -60,16 +124,25 @@ const NavHeader = ({ fairground }: { fairground: boolean }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-8">
|
||||
<Link to="/">
|
||||
{fairground ? (
|
||||
<Fish />
|
||||
) : (
|
||||
<img alt="Vega" src={vegaWhite} height={30} width={30} />
|
||||
)}
|
||||
</Link>
|
||||
<div className="h-[30px] inline-flex items-center lg:h-40 uppercase">
|
||||
<h1
|
||||
data-testid="header-title"
|
||||
className={`text-h3 font-alpha uppercase calt mb-2 ${
|
||||
className={`text-h4 sm:text-h3 font-alpha uppercase calt mb-2 ${
|
||||
fairground ? 'text-black' : 'text-white'
|
||||
}`}
|
||||
>
|
||||
{fairground ? t('fairgroundTitle') : t('title')}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -135,7 +208,13 @@ const NavDrawer = ({ inverted }: { inverted: boolean }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const NavLinks = ({ isDesktop }: { isDesktop: boolean }) => {
|
||||
const NavLinks = ({
|
||||
isDesktop,
|
||||
isInverted,
|
||||
}: {
|
||||
isDesktop: boolean;
|
||||
isInverted?: boolean;
|
||||
}) => {
|
||||
const { appDispatch } = useAppState();
|
||||
const { t } = useTranslation();
|
||||
const linkProps = {
|
||||
@ -163,18 +242,21 @@ const NavLinks = ({ isDesktop }: { isDesktop: boolean }) => {
|
||||
{...linkProps}
|
||||
to={route}
|
||||
key={route}
|
||||
className={({ isActive }) => {
|
||||
const linkClasses = classNames(
|
||||
'no-underline hover:no-underline',
|
||||
className={({ isActive }) =>
|
||||
classNames(
|
||||
'no-underline hover:no-underline focus-visible:outline-none focus-visible:border-none focus-visible:shadow-inset-white',
|
||||
{
|
||||
'bg-vega-yellow text-black hover:text-black': isActive,
|
||||
'bg-black text-white': !isActive,
|
||||
'bg-vega-yellow text-black': !isInverted && isActive,
|
||||
'bg-transparent text-white hover:text-vega-yellow':
|
||||
!isInverted && !isActive,
|
||||
'bg-black text-white': isInverted && isActive,
|
||||
'bg-transparent text-black hover:text-white':
|
||||
isInverted && !isActive,
|
||||
'py-2 px-12': isDesktop,
|
||||
'border-t border-white p-20': !isDesktop,
|
||||
}
|
||||
);
|
||||
return linkClasses;
|
||||
}}
|
||||
)
|
||||
}
|
||||
>
|
||||
{text}
|
||||
</NavLink>
|
||||
|
@ -23,12 +23,8 @@ export const TransactionComplete = ({
|
||||
intent={Intent.Success}
|
||||
title={heading || t('Complete')}
|
||||
>
|
||||
{body && (
|
||||
<p className="mb-8" data-testid="transaction-complete-body">
|
||||
{body}
|
||||
</p>
|
||||
)}
|
||||
<p className="mb-8">
|
||||
{body && <p data-testid="transaction-complete-body">{body}</p>}
|
||||
<p>
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
target="_blank"
|
||||
|
@ -20,11 +20,9 @@ export const TransactionError = ({
|
||||
|
||||
return (
|
||||
<Callout iconName="error" intent={Intent.Danger}>
|
||||
<p className="mb-8">
|
||||
{error ? error.message : t('Something went wrong')}
|
||||
</p>
|
||||
<p>{error ? error.message : t('Something went wrong')}</p>
|
||||
{hash ? (
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
href={`${ETHERSCAN_URL}/tx/${hash}`}
|
||||
|
@ -38,12 +38,8 @@ export const TransactionPending = ({
|
||||
}, [heading, remainingConfirmations, t]);
|
||||
return (
|
||||
<Callout icon={<Loader size="small" />} title={title}>
|
||||
{body && (
|
||||
<p className="mb-8" data-testid="transaction-pending-body">
|
||||
{body}
|
||||
</p>
|
||||
)}
|
||||
<p className="mb-8">
|
||||
{body && <p data-testid="transaction-pending-body">{body}</p>}
|
||||
<p>
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
target="_blank"
|
||||
|
@ -5,18 +5,18 @@ import { Links } from '../../config';
|
||||
export const DownloadWalletPrompt = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="mt-8">
|
||||
<h3>{t('getWallet')}</h3>
|
||||
<p>
|
||||
<>
|
||||
<h3 className="mt-12 mb-4">{t('getWallet')}</h3>
|
||||
<p className="mb-4">
|
||||
<Link className="text-deemphasise" href={Links.WALLET_GUIDE}>
|
||||
{t('readGuide')}
|
||||
</Link>
|
||||
</p>
|
||||
<p>
|
||||
<p className="mb-4">
|
||||
<Link className="text-deemphasise" href={Links.WALLET_RELEASES}>
|
||||
{t('downloadWallet')}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -40,14 +40,16 @@ export const VegaWallet = () => {
|
||||
<section className="vega-wallet">
|
||||
<WalletCard dark={true}>
|
||||
<WalletCardHeader dark={true}>
|
||||
<div>
|
||||
<h1 className="text-h3 uppercase">{t('vegaWallet')}</h1>
|
||||
<span className="text-h6">{keypair && `(${keypair.name})`}</span>
|
||||
</div>
|
||||
<h1 className="col-start-1 m-0">{t('vegaWallet')}</h1>
|
||||
{keypair && (
|
||||
<span className="font-mono px-8">
|
||||
<>
|
||||
<div className="sm:row-start-2 sm:col-start-1 sm:col-span-2 text-h6 mb-12">
|
||||
{keypair.name}
|
||||
</div>
|
||||
<span className="sm:col-start-2 place-self-end font-mono pb-2 px-4">
|
||||
{truncateMiddle(keypair.pub)}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</WalletCardHeader>
|
||||
<WalletCardContent>{child}</WalletCardContent>
|
||||
@ -128,6 +130,7 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
|
||||
<WalletCardActions>
|
||||
<Button
|
||||
variant="inline-link"
|
||||
className="mt-4"
|
||||
onClick={() =>
|
||||
appDispatch({
|
||||
type: AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY,
|
||||
|
@ -29,10 +29,14 @@ interface WalletCardProps {
|
||||
}
|
||||
|
||||
export const WalletCard = ({ dark, children }: WalletCardProps) => {
|
||||
const className = classNames('text-ui border border-white', 'p-8', {
|
||||
const className = classNames(
|
||||
'text-ui border border-white',
|
||||
'pt-4 pl-8 pr-12 pb-12',
|
||||
{
|
||||
'bg-black text-white': dark,
|
||||
'bg-white text-black': !dark,
|
||||
});
|
||||
}
|
||||
);
|
||||
return <div className={className}>{children}</div>;
|
||||
};
|
||||
|
||||
@ -43,7 +47,9 @@ interface WalletCardHeaderProps {
|
||||
|
||||
export const WalletCardHeader = ({ children }: WalletCardHeaderProps) => {
|
||||
return (
|
||||
<div className="flex gap-4 justify-between items-center">{children}</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-[auto_1fr] gap-4">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -145,23 +151,23 @@ export const WalletCardAsset = ({
|
||||
}`}
|
||||
/>
|
||||
<div>
|
||||
<div className="flex font-medium">
|
||||
<div className="flex font-medium align-center">
|
||||
<h1
|
||||
className={`text-h5 px-8 uppercase ${
|
||||
className={`text-h5 mb-0 px-8 uppercase leading-none ${
|
||||
dark ? 'text-white' : 'text-black'
|
||||
}`}
|
||||
>
|
||||
{name}
|
||||
</h1>
|
||||
<h2
|
||||
className={`text-h5 uppercase ${
|
||||
className={`text-h5 mb-0 uppercase leading-none ${
|
||||
dark ? 'text-white-60' : 'text-black-60'
|
||||
}`}
|
||||
>
|
||||
{subheading || symbol}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="px-8 text-h5 basis-full">
|
||||
<div className="px-8 text-h5 basis-full font-mono">
|
||||
<span>{integers}.</span>
|
||||
<span className={dark ? 'text-white-60' : 'text-black-60'}>
|
||||
{decimalsPlaces}
|
||||
|
@ -79,7 +79,7 @@ export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
|
||||
return (
|
||||
<Splash>
|
||||
<div className="flex flex-col items-center gap-12">
|
||||
<p className="mb-12">This app only works on chain ID: {appChainId}</p>
|
||||
<p>This app only works on chain ID: {appChainId}</p>
|
||||
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
|
||||
</div>
|
||||
</Splash>
|
||||
|
@ -10,7 +10,7 @@ export const ProposalTermsJson = ({
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<section>
|
||||
<h2 className="text-h4 text-white mb-8">{t('proposalTerms')}</h2>
|
||||
<h2>{t('proposalTerms')}</h2>
|
||||
<SyntaxHighlighter data={terms} />
|
||||
</section>
|
||||
);
|
||||
|
@ -23,10 +23,10 @@ export const ProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||
return (
|
||||
<>
|
||||
<Heading title={t('pageTitleGovernance')} />
|
||||
<p className="mb-8">{t('proposedChangesToVegaNetwork')}</p>
|
||||
<p className="mb-8">{t('vegaTokenHoldersCanVote')}</p>
|
||||
<p className="mb-8">{t('requiredMajorityDescription')}</p>
|
||||
<h2 className="text-h4 text-white">{t('proposals')}</h2>
|
||||
<p>{t('proposedChangesToVegaNetwork')}</p>
|
||||
<p>{t('vegaTokenHoldersCanVote')}</p>
|
||||
<p>{t('requiredMajorityDescription')}</p>
|
||||
<h2>{t('proposals')}</h2>
|
||||
<ul>
|
||||
{proposals.map((proposal) => (
|
||||
<ProposalListItem proposal={proposal} />
|
||||
|
@ -45,7 +45,7 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
|
||||
return (
|
||||
<section>
|
||||
<h3 className="text-h4 text-white mb-8">{t('votes')}</h3>
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
<span>
|
||||
<CurrentProposalStatus proposal={proposal} />
|
||||
</span>
|
||||
@ -90,7 +90,7 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
{t('participation')}
|
||||
{': '}
|
||||
{participationMet ? (
|
||||
|
@ -42,13 +42,13 @@ const Home = ({ name }: RouteChildProps) => {
|
||||
/>
|
||||
</HomeSection>
|
||||
<HomeSection>
|
||||
<h2 className="text-h4 text-white">{t('Token Vesting')}</h2>
|
||||
<p className="mb-8">
|
||||
<h2>{t('Token Vesting')}</h2>
|
||||
<p>
|
||||
{t(
|
||||
'The vesting contract holds VEGA tokens until they have become unlocked.'
|
||||
)}
|
||||
</p>
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="Tokens are held in different <trancheLink>Tranches</trancheLink>. Each tranche has its own schedule for how the tokens are unlocked."
|
||||
components={{
|
||||
@ -62,7 +62,7 @@ const Home = ({ name }: RouteChildProps) => {
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
{t(
|
||||
'Once unlocked they can be redeemed from the contract so that you can transfer them between wallets.'
|
||||
)}
|
||||
@ -74,13 +74,13 @@ const Home = ({ name }: RouteChildProps) => {
|
||||
</Link>
|
||||
</HomeSection>
|
||||
<HomeSection>
|
||||
<h2 className="text-h4 text-white">{t('Use your Vega tokens')}</h2>
|
||||
<p className="mb-8">
|
||||
<h2 className="uppercase">{t('Use your Vega tokens')}</h2>
|
||||
<p>
|
||||
{t(
|
||||
'To use your tokens on the Vega network they need to be associated with a Vega wallet/key.'
|
||||
)}
|
||||
</p>
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
{t(
|
||||
'This can happen both while held in the vesting contract as well as when redeemed.'
|
||||
)}
|
||||
@ -109,8 +109,8 @@ const Home = ({ name }: RouteChildProps) => {
|
||||
<div className="flex gap-40">
|
||||
<div className="flex-1">
|
||||
<HomeSection>
|
||||
<h2 className="text-h4 text-white">{t('Staking')}</h2>
|
||||
<p className="mb-8">
|
||||
<h2>{t('Staking')}</h2>
|
||||
<p>
|
||||
{t(
|
||||
'VEGA token holders can nominate a validator node and receive staking rewards.'
|
||||
)}
|
||||
@ -129,8 +129,8 @@ const Home = ({ name }: RouteChildProps) => {
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<HomeSection>
|
||||
<h2 className="text-h4 text-white">{t('Governance')}</h2>
|
||||
<p className="mb-8">
|
||||
<h2>{t('Governance')}</h2>
|
||||
<p>
|
||||
{t(
|
||||
'VEGA token holders can vote on proposed changes to the network and create proposals.'
|
||||
)}
|
||||
@ -155,5 +155,5 @@ const Home = ({ name }: RouteChildProps) => {
|
||||
export default Home;
|
||||
|
||||
export const HomeSection = ({ children }: { children: React.ReactNode }) => {
|
||||
return <section className="mb-20">{children}</section>;
|
||||
return <section className="mb-28">{children}</section>;
|
||||
};
|
||||
|
@ -37,7 +37,7 @@ export const TokenDetailsCirculating = ({
|
||||
}) => {
|
||||
const totalCirculating = sumCirculatingTokens(tranches);
|
||||
return (
|
||||
<span data-testid="circulating-supply">
|
||||
<span data-testid="circulating-supply" className="font-mono">
|
||||
{formatNumber(totalCirculating, 2)}
|
||||
</span>
|
||||
);
|
||||
|
@ -42,13 +42,13 @@ export const TokenDetails = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<KeyValueTable className={'token-details'}>
|
||||
<KeyValueTable className={'token-details text-white'}>
|
||||
<KeyValueTableRow>
|
||||
{t('Token address').toUpperCase()}
|
||||
<Link
|
||||
data-testid="token-address"
|
||||
title={t('View on Etherscan (opens in a new tab)')}
|
||||
className="font-mono"
|
||||
className="font-mono text-white text-right"
|
||||
href={`${ETHERSCAN_URL}/address/${token.address}`}
|
||||
>
|
||||
{token.address}
|
||||
@ -59,7 +59,7 @@ export const TokenDetails = ({
|
||||
<Link
|
||||
data-testid="token-contract"
|
||||
title={t('View on Etherscan (opens in a new tab)')}
|
||||
className="font-mono"
|
||||
className="font-mono text-white text-right"
|
||||
href={`${ETHERSCAN_URL}/address/${config.token_vesting_contract.address}`}
|
||||
>
|
||||
{config.token_vesting_contract.address}
|
||||
@ -67,7 +67,9 @@ export const TokenDetails = ({
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('Total supply').toUpperCase()}
|
||||
<span data-testid="total-supply">{formatNumber(totalSupply, 2)}</span>
|
||||
<span className="font-mono" data-testid="total-supply">
|
||||
{formatNumber(totalSupply, 2)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('Circulating supply').toUpperCase()}
|
||||
@ -75,7 +77,9 @@ export const TokenDetails = ({
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('Staked on Vega validator').toUpperCase()}
|
||||
<span data-testid="staked">{formatNumber(totalStaked, 2)}</span>
|
||||
<span data-testid="staked" className="font-mono">
|
||||
{formatNumber(totalStaked, 2)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
);
|
||||
|
@ -55,7 +55,8 @@ export const RedemptionInformation = () => {
|
||||
if (!filteredTranches.length) {
|
||||
return (
|
||||
<section data-testid="redemption-page">
|
||||
<p className="mb-12" data-testid="redemption-no-balance">
|
||||
<div className="mb-20">
|
||||
<p data-testid="redemption-no-balance">
|
||||
<Trans
|
||||
i18nKey="noVestingTokens"
|
||||
components={{
|
||||
@ -65,6 +66,7 @@ export const RedemptionInformation = () => {
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<AddLockedTokenAddress />
|
||||
</section>
|
||||
);
|
||||
@ -92,9 +94,7 @@ export const RedemptionInformation = () => {
|
||||
vested={totalVestedBalance}
|
||||
/>
|
||||
</div>
|
||||
{filteredTranches.length ? (
|
||||
<h2 className="text-h4 text-white mb-12">{t('Tranche breakdown')}</h2>
|
||||
) : null}
|
||||
{filteredTranches.length ? <h2>{t('Tranche breakdown')}</h2> : null}
|
||||
{zeroTranche && (
|
||||
<Tranche0Table
|
||||
trancheId={0}
|
||||
@ -133,7 +133,7 @@ export const RedemptionInformation = () => {
|
||||
iconName="hand-up"
|
||||
intent={Intent.Warning}
|
||||
>
|
||||
<p className="mb-12">{t('Find out more about Staking.')}</p>
|
||||
<p>{t('Find out more about Staking.')}</p>
|
||||
<Link to="/staking" className="underline text-white">
|
||||
{t('Stake VEGA tokens')}
|
||||
</Link>
|
||||
|
@ -68,7 +68,7 @@ const RedemptionRouter = () => {
|
||||
if (!account) {
|
||||
return (
|
||||
<EthConnectPrompt>
|
||||
<p data-testid="eth-connect-prompt" className="mb-8">
|
||||
<p data-testid="eth-connect-prompt">
|
||||
{t(
|
||||
"Use the Ethereum wallet you want to send your tokens to. You'll also need enough Ethereum to pay gas."
|
||||
)}
|
||||
|
@ -73,7 +73,7 @@ export const RedeemFromTranche = () => {
|
||||
!trancheBalance
|
||||
) {
|
||||
return (
|
||||
<section data-testid="redemption-page">
|
||||
<section data-testid="redemption-page" className="mb-28">
|
||||
<p data-testid="redemption-no-balance">
|
||||
<Trans
|
||||
i18nKey="noVestingTokens"
|
||||
@ -99,7 +99,7 @@ export const RedeemFromTranche = () => {
|
||||
}
|
||||
completeBody={
|
||||
<>
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
{t(
|
||||
'You have redeemed {{redeemedAmount}} VEGA tokens from this tranche. They are now free to transfer from your Ethereum wallet.',
|
||||
{
|
||||
@ -107,7 +107,7 @@ export const RedeemFromTranche = () => {
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
{t(
|
||||
'The VEGA token address is {{address}}, make sure you add this to your wallet to see your tokens',
|
||||
{
|
||||
|
@ -109,8 +109,8 @@ export const RewardsIndex = () => {
|
||||
return (
|
||||
<section className="rewards">
|
||||
<Heading title={t('pageTitleRewards')} />
|
||||
<p className="mb-8">{t('rewardsPara1')}</p>
|
||||
<p className="mb-8">{t('rewardsPara2')}</p>
|
||||
<p>{t('rewardsPara1')}</p>
|
||||
<p>{t('rewardsPara2')}</p>
|
||||
{payoutDuration ? (
|
||||
<div className="my-24">
|
||||
<Callout
|
||||
|
@ -64,7 +64,7 @@ export const RewardInfo = ({
|
||||
|
||||
return (
|
||||
<div className="mt-24">
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
{t('Connected Vega key')}: {currVegaKey.pub}
|
||||
</p>
|
||||
{vegaTokenRewards.length ? (
|
||||
|
@ -13,7 +13,7 @@ export const AssociateInfo = ({ pubKey }: { pubKey: string | null }) => {
|
||||
{t('What Vega key is going to control your stake?')}
|
||||
</h2>
|
||||
<ConnectedVegaKey pubKey={pubKey} />
|
||||
<h2 className="text-h4 text-white" data-testid="associate-amount-header">
|
||||
<h2 data-testid="associate-amount-header">
|
||||
{t('How much would you like to associate?')}
|
||||
</h2>
|
||||
</>
|
||||
|
@ -62,13 +62,13 @@ export const AssociateTransaction = ({
|
||||
if (derivedTxState === TxState.Pending) {
|
||||
return (
|
||||
<Callout icon={<Loader size="small" />} title={title}>
|
||||
<p data-testid="transaction-pending-body" className="mb-8">
|
||||
<p data-testid="transaction-pending-body">
|
||||
{t('Associating {{amount}} VEGA tokens with Vega key {{vegaKey}}', {
|
||||
amount,
|
||||
vegaKey: truncateMiddle(vegaKey),
|
||||
})}
|
||||
</p>
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
href={`${ETHERSCAN_URL}/tx/${state.txData.hash}`}
|
||||
@ -77,7 +77,7 @@ export const AssociateTransaction = ({
|
||||
{t('View on Etherscan (opens in a new tab)')}
|
||||
</Link>
|
||||
</p>
|
||||
<p data-testid="transaction-pending-footer" className="mb-8">
|
||||
<p data-testid="transaction-pending-footer">
|
||||
{t('pendingAssociationText', {
|
||||
confirmations: requiredConfirmations,
|
||||
})}
|
||||
|
@ -60,24 +60,20 @@ export const DisassociatePage = ({
|
||||
|
||||
return (
|
||||
<section className="disassociate-page" data-testid="disassociate-page">
|
||||
<p className="mb-12">
|
||||
<p>
|
||||
{t(
|
||||
'Use this form to disassociate VEGA tokens with a Vega key. This returns them to either the Ethereum wallet that used the Staking bridge or the vesting contract.'
|
||||
)}
|
||||
</p>
|
||||
<p className="mb-12">
|
||||
<p>
|
||||
<span className="text-vega-red">{t('Warning')}:</span>{' '}
|
||||
{t(
|
||||
'Any Tokens that have been nominated to a node will sacrifice any Rewards they are due for the current epoch. If you do not wish to sacrifices fees you should remove stake from a node at the end of an epoch before disassocation.'
|
||||
)}
|
||||
</p>
|
||||
<h2 className="text-h4 text-white mb-8">
|
||||
{t('What Vega wallet are you removing Tokens from?')}
|
||||
</h2>
|
||||
<h2>{t('What Vega wallet are you removing Tokens from?')}</h2>
|
||||
<ConnectedVegaKey pubKey={vegaKey.pub} />
|
||||
<h2 className="text-h4 text-white mb-8">
|
||||
{t('What tokens would you like to return?')}
|
||||
</h2>
|
||||
<h2>{t('What tokens would you like to return?')}</h2>
|
||||
<StakingMethodRadio
|
||||
setSelectedStakingMethod={setSelectedStakingMethod}
|
||||
selectedStakingMethod={selectedStakingMethod}
|
||||
|
@ -223,7 +223,7 @@ export const StakingForm = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="text-h4 mb-8">{t('Manage your stake')}</h2>
|
||||
<h2>{t('Manage your stake')}</h2>
|
||||
<FormGroup>
|
||||
<RadioGroup
|
||||
onChange={(value) => {
|
||||
@ -254,9 +254,7 @@ export const StakingForm = ({
|
||||
<>
|
||||
{action === Actions.Add ? (
|
||||
<>
|
||||
<h2 className="text-h4 mb-8">
|
||||
{t('How much to Add in next epoch?')}
|
||||
</h2>
|
||||
<h2>{t('How much to Add in next epoch?')}</h2>
|
||||
<p>
|
||||
{t('minimumNomination', {
|
||||
minTokens: minTokensWithDecimals,
|
||||
|
@ -96,10 +96,7 @@ export const StakingNode = ({ vegaKey, data }: StakingNodeProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2
|
||||
data-test-id="validator-node-title"
|
||||
className="text-h4 break-word mb-8"
|
||||
>
|
||||
<h2 data-test-id="validator-node-title" className="text-h4 break-word">
|
||||
{nodeInfo.name
|
||||
? t('validatorTitle', { nodeName: nodeInfo.name })
|
||||
: t('validatorTitle', { nodeName: t('validatorTitleFallback') })}
|
||||
|
@ -19,17 +19,19 @@ import { NodeList } from './node-list';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { truncateMiddle } from '../../lib/truncate-middle';
|
||||
|
||||
const stakingBulletStyles = { marginBottom: '12px', fontSize: '18px' };
|
||||
|
||||
export const Staking = ({ data }: { data?: StakingQueryResult }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="mb-24">
|
||||
<p className="mb-12">{t('stakingDescription1')}</p>
|
||||
<p className="mb-12">{t('stakingDescription2')}</p>
|
||||
<p className="mb-12">{t('stakingDescription3')}</p>
|
||||
<p className="mb-12">{t('stakingDescription4')}</p>
|
||||
<p className="mb-12">
|
||||
<p>{t('stakingDescription1')}</p>
|
||||
<p>{t('stakingDescription2')}</p>
|
||||
<p>{t('stakingDescription3')}</p>
|
||||
<p>{t('stakingDescription4')}</p>
|
||||
<p>
|
||||
<Link
|
||||
href={Links.STAKING_GUIDE}
|
||||
className="text-white underline"
|
||||
@ -41,11 +43,15 @@ export const Staking = ({ data }: { data?: StakingQueryResult }) => {
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<BulletHeader tag="h2">{t('stakingStep1')}</BulletHeader>
|
||||
<BulletHeader tag="h2" style={stakingBulletStyles}>
|
||||
{t('stakingStep1')}
|
||||
</BulletHeader>
|
||||
<StakingStepConnectWallets />
|
||||
</section>
|
||||
<section>
|
||||
<BulletHeader tag="h2">{t('stakingStep1')}</BulletHeader>
|
||||
<BulletHeader tag="h2" style={stakingBulletStyles}>
|
||||
{t('stakingStep2')}
|
||||
</BulletHeader>
|
||||
<StakingStepAssociate
|
||||
associated={
|
||||
new BigNumber(
|
||||
@ -55,7 +61,9 @@ export const Staking = ({ data }: { data?: StakingQueryResult }) => {
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<BulletHeader tag="h2">{t('stakingStep3')}</BulletHeader>
|
||||
<BulletHeader tag="h2" style={stakingBulletStyles}>
|
||||
{t('stakingStep3')}
|
||||
</BulletHeader>
|
||||
<StakingStepSelectNode data={data} />
|
||||
</section>
|
||||
</>
|
||||
@ -82,7 +90,7 @@ export const StakingStepConnectWallets = () => {
|
||||
{truncateMiddle(account)}
|
||||
</Link>
|
||||
</p>
|
||||
<p>
|
||||
<p className="mb-8">
|
||||
{t('stakingVegaWalletConnected', {
|
||||
key: truncateMiddle(keypair.pub),
|
||||
})}
|
||||
@ -93,7 +101,7 @@ export const StakingStepConnectWallets = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="stakingStep1Text"
|
||||
components={{
|
||||
@ -116,7 +124,7 @@ export const StakingStepConnectWallets = () => {
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
<Button
|
||||
onClick={() =>
|
||||
appDispatch({
|
||||
|
@ -17,7 +17,7 @@ export const YourStake = ({
|
||||
|
||||
return (
|
||||
<div data-testid="your-stake">
|
||||
<h2 className="text-h4 mb-8">{t('Your stake')}</h2>
|
||||
<h2>{t('Your stake')}</h2>
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow>
|
||||
{t('Your Stake On Node (This Epoch)')}
|
||||
|
@ -67,7 +67,7 @@ export const Tranche = () => {
|
||||
{formatNumber(tranche.total_removed)}
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="text-h4 text-white mb-8">{t('Holders')}</h2>
|
||||
<h2>{t('Holders')}</h2>
|
||||
{tranche.users.length ? (
|
||||
<ul role="list">
|
||||
{tranche.users.map((user, i) => {
|
||||
|
@ -29,10 +29,10 @@ export const Tranches = () => {
|
||||
|
||||
return (
|
||||
<section>
|
||||
<h2 className="text-h4">{t('chartTitle')}</h2>
|
||||
<p className="mb-12">{t('chartAbove')}</p>
|
||||
<h2>{t('chartTitle')}</h2>
|
||||
<p>{t('chartAbove')}</p>
|
||||
<VestingChart />
|
||||
<p className="mb-12">{t('chartBelow')}</p>
|
||||
<p>{t('chartBelow')}</p>
|
||||
{tranches?.length ? (
|
||||
<ul role="list">
|
||||
{(showAll ? tranches : filteredTranches).map((tranche) => {
|
||||
|
@ -23,7 +23,7 @@ const Withdraw = () => {
|
||||
return (
|
||||
<>
|
||||
<Heading title={t('withdrawPageHeading')} />
|
||||
<p className="mb-8">{t('withdrawPageText')}</p>
|
||||
<p>{t('withdrawPageText')}</p>
|
||||
<div className="mb-24">
|
||||
<VegaWalletContainer>
|
||||
{(currVegaKey) => <WithdrawContainer currVegaKey={currVegaKey} />}
|
||||
@ -141,7 +141,7 @@ export const WithdrawContainer = ({ currVegaKey }: WithdrawContainerProps) => {
|
||||
title={t('pendingWithdrawalsCalloutTitle')}
|
||||
intent={Intent.Warning}
|
||||
>
|
||||
<p className="mb-8">{t('pendingWithdrawalsCalloutText')}</p>
|
||||
<p>{t('pendingWithdrawalsCalloutText')}</p>
|
||||
<p>
|
||||
<Link to={Routes.WITHDRAWALS} className="underline text-white">
|
||||
{t('pendingWithdrawalsCalloutButton')}
|
||||
|
@ -74,7 +74,7 @@ const WithdrawPendingContainer = () => {
|
||||
return (
|
||||
<>
|
||||
<h2>{t('withdrawalsPreparedWarningHeading')}</h2>
|
||||
<p className="mb-8">{t('withdrawalsText')}</p>
|
||||
<p>{t('withdrawalsText')}</p>
|
||||
<p className="mb-28">{t('withdrawalsPreparedWarningText')}</p>
|
||||
<ul role="list">
|
||||
{withdrawals.map((w) => (
|
||||
|
@ -1,3 +1,15 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
h1 {
|
||||
@apply text-h3 text-white uppercase mb-12;
|
||||
}
|
||||
h2 {
|
||||
@apply text-h4 text-white mb-8;
|
||||
}
|
||||
p {
|
||||
@apply mb-12;
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"stepDefinitions": "src/support/step_definitions"
|
||||
}
|
48
apps/trading-e2e/cypress.config.js
Normal file
48
apps/trading-e2e/cypress.config.js
Normal file
@ -0,0 +1,48 @@
|
||||
const { defineConfig } = require('cypress');
|
||||
|
||||
module.exports = defineConfig({
|
||||
component: {
|
||||
baseUrl: 'http://localhost:4200',
|
||||
fileServerFolder: '.',
|
||||
fixturesFolder: false,
|
||||
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
|
||||
supportFile: './src/support/index.ts',
|
||||
video: true,
|
||||
videosFolder: '../../dist/cypress/apps/trading-e2e/videos',
|
||||
screenshotsFolder: '../../dist/cypress/apps/trading-e2e/screenshots',
|
||||
chromeWebSecurity: false,
|
||||
projectId: 'et4snf',
|
||||
defaultCommandTimeout: 10000,
|
||||
},
|
||||
e2e: {
|
||||
baseUrl: 'http://localhost:4200',
|
||||
fileServerFolder: '.',
|
||||
fixturesFolder: false,
|
||||
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
|
||||
supportFile: './src/support/index.ts',
|
||||
video: true,
|
||||
videosFolder: '../../dist/cypress/apps/trading-e2e/videos',
|
||||
screenshotsFolder: '../../dist/cypress/apps/trading-e2e/screenshots',
|
||||
chromeWebSecurity: false,
|
||||
projectId: 'et4snf',
|
||||
defaultCommandTimeout: 10000,
|
||||
},
|
||||
env: {
|
||||
TRADING_TEST_VEGA_WALLET_NAME: 'UI_Trading_Test',
|
||||
ETHEREUM_PROVIDER_URL:
|
||||
'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
|
||||
VEGA_PUBLIC_KEY:
|
||||
'47836c253520d2661bf5bed6339c0de08fd02cf5d4db0efee3b4373f20c7d278',
|
||||
VEGA_PUBLIC_KEY2:
|
||||
'1a18cdcaaa4f44a57b35a4e9b77e0701c17a476f2b407620f8c17371740cf2e4',
|
||||
TRUNCATED_VEGA_PUBLIC_KEY: '47836c…c7d278',
|
||||
TRUNCATED_VEGA_PUBLIC_KEY2: '1a18cd…0cf2e4',
|
||||
ETHEREUM_WALLET_ADDRESS: '0x265Cc6d39a1B53d0d92068443009eE7410807158',
|
||||
ETHERSCAN_URL: 'https://ropsten.etherscan.io',
|
||||
tsConfig: 'tsconfig.json',
|
||||
TAGS: 'not @todo and not @ignore and not @manual',
|
||||
TRADING_TEST_VEGA_WALLET_PASSPHRASE: '123',
|
||||
ETH_WALLET_MNEMONIC:
|
||||
'ugly gallery notice network true range brave clarify flat logic someone chunk',
|
||||
},
|
||||
});
|
@ -1,31 +0,0 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:4200",
|
||||
"fileServerFolder": ".",
|
||||
"fixturesFolder": false,
|
||||
"pluginsFile": "./src/plugins/index.js",
|
||||
"testFiles": "*.{ts,feature,features}",
|
||||
"ignoreTestFiles": "**/*.js",
|
||||
"integrationFolder": "./src/integration",
|
||||
"modifyObstructiveCode": false,
|
||||
"supportFile": "./src/support/index.ts",
|
||||
"video": true,
|
||||
"videosFolder": "../../dist/cypress/apps/trading-e2e/videos",
|
||||
"screenshotsFolder": "../../dist/cypress/apps/trading-e2e/screenshots",
|
||||
"chromeWebSecurity": false,
|
||||
"projectId": "et4snf",
|
||||
"defaultCommandTimeout": 10000,
|
||||
"env": {
|
||||
"TRADING_TEST_VEGA_WALLET_NAME": "UI_Trading_Test",
|
||||
"ETHEREUM_PROVIDER_URL": "https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8",
|
||||
"VEGA_PUBLIC_KEY": "47836c253520d2661bf5bed6339c0de08fd02cf5d4db0efee3b4373f20c7d278",
|
||||
"VEGA_PUBLIC_KEY2": "1a18cdcaaa4f44a57b35a4e9b77e0701c17a476f2b407620f8c17371740cf2e4",
|
||||
"TRUNCATED_VEGA_PUBLIC_KEY": "47836c…c7d278",
|
||||
"TRUNCATED_VEGA_PUBLIC_KEY2": "1a18cd…0cf2e4",
|
||||
"ETHEREUM_WALLET_ADDRESS": "0x265Cc6d39a1B53d0d92068443009eE7410807158",
|
||||
"ETHERSCAN_URL": "https://ropsten.etherscan.io",
|
||||
"tsConfig": "tsconfig.json",
|
||||
"TAGS": "not @todo and not @ignore and not @manual",
|
||||
"TRADING_TEST_VEGA_WALLET_PASSPHRASE": "123",
|
||||
"ETH_WALLET_MNEMONIC": "ugly gallery notice network true range brave clarify flat logic someone chunk"
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
"e2e": {
|
||||
"executor": "@nrwl/cypress:cypress",
|
||||
"options": {
|
||||
"cypressConfig": "apps/trading-e2e/cypress.json",
|
||||
"cypressConfig": "apps/trading-e2e/cypress.config.js",
|
||||
"devServerTarget": "trading:serve"
|
||||
},
|
||||
"configurations": {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user