Compare commits

..
Author SHA1 Message Date
Botond 01320f9748 fix: format 2022-08-26 19:39:10 +02:00
Botond 91cdd434b6 fix: imports 2022-08-26 19:38:15 +02:00
Botond ed8b053d7b fix: format 2022-08-26 18:55:11 +02:00
Botond 8ae5b66824 feat: add network config override 2022-08-26 18:54:33 +02:00
Botond 563021aa0b feat: add network update 2022-08-26 15:52:17 +02:00
Botond 13c35a5f65 chore: merge source 2022-08-26 13:38:26 +02:00
Botond ee1223c9f6 fix: add missing utils 2022-08-26 13:37:50 +02:00
Botond c368d0b546 chore: merge source 2022-08-26 13:33:52 +02:00
Botond ad06481408 fix: format 2022-08-26 13:33:19 +02:00
Botond a928669fc7 feat: merge source 2022-08-26 13:32:50 +02:00
Botond 69d3313f90 fix: format 2022-08-25 18:08:52 +02:00
Botond 04c1d68333 feat: add type update script 2022-08-25 17:47:40 +02:00
1008 changed files with 17684 additions and 39831 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
apps/**/dist/*
apps/**/node_modules/*
dist/*
node_modules/*
tmp/*
.dockerignore
dockerfiles
Dockerfile
@@ -47,14 +47,6 @@ jobs:
## Checkout capsule
#######
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
path: './frontend-monorepo'
# Checkout capsule to build local network
- name: Checkout capsule
uses: actions/checkout@v2
@@ -78,7 +70,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: vegaprotocol/vega
ref: v0.54.0
ref: develop
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vega'
@@ -104,7 +96,7 @@ jobs:
run: vegacapsule nomad &
- name: Bootstrap network
run: vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
run: vegacapsule network bootstrap --config-path=./net_confs/config.hcl --force
working-directory: capsule
######
@@ -128,13 +120,18 @@ jobs:
## Run some tests
######
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- name: Install root dependencies
run: yarn install
working-directory: frontend-monorepo
- name: Run Cypress tests
run: yarn nx run ${{ github.event.inputs.project }}:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
working-directory: frontend-monorepo
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
@@ -31,14 +31,6 @@ jobs:
## Checkout capsule
#######
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
path: './frontend-monorepo'
# Checkout capsule to build local network
- name: Checkout capsule
uses: actions/checkout@v2
@@ -62,7 +54,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: vegaprotocol/vega
ref: v0.54.0
ref: develop
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vega'
@@ -88,7 +80,7 @@ jobs:
run: vegacapsule nomad &
- name: Bootstrap network
run: vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
run: vegacapsule network bootstrap --config-path=./net_confs/config.hcl --force
working-directory: capsule
######
@@ -112,13 +104,18 @@ jobs:
## Run some tests
######
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
ref: master
fetch-depth: 0
- name: Install root dependencies
run: yarn install
working-directory: frontend-monorepo
- name: Run Cypress tests
run: yarn nx run-many --target=e2e --all --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
working-directory: frontend-monorepo
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
+14 -23
View File
@@ -6,17 +6,12 @@ on:
- master
- develop
pull_request:
types:
- opened
- reopened
- synchronize
- ready_for_review
jobs:
pr:
name: Run capsule tests - PR
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft == false }}
if: ${{ github.event_name == 'pull_request' }}
env:
GO111MODULE: 'on'
steps:
@@ -38,20 +33,12 @@ jobs:
## Checkout capsule
#######
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
path: './frontend-monorepo'
# Checkout capsule to build local network
- name: Checkout capsule
uses: actions/checkout@v2
with:
repository: vegaprotocol/vegacapsule
ref: v0.2.3
ref: main
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './capsule'
@@ -69,7 +56,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: vegaprotocol/vega
ref: v0.54.0
ref: develop
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vega'
@@ -95,7 +82,7 @@ jobs:
run: vegacapsule nomad &
- name: Bootstrap network
run: vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
run: vegacapsule network bootstrap --config-path=./net_confs/config.hcl --force
working-directory: capsule
######
@@ -119,19 +106,23 @@ jobs:
## Run some tests
######
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v3
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
working-directory: frontend-monorepo
main-branch-name: main
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v2
with:
main-branch-name: master
- name: Install root dependencies
run: yarn install
working-directory: frontend-monorepo
- name: Run Cypress tests
run: npx nx affected:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
working-directory: frontend-monorepo
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
@@ -0,0 +1,60 @@
name: Publish NextJs containers
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-pre[0-9]+'
jobs:
master:
strategy:
matrix:
build-platform: ['linux/amd64']
app: ['trading']
name: Build the ${{ inputs.image_name }} image
runs-on: ubuntu-latest
steps:
- name: Set output
id: vars
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
- name: Check output
env:
RELEASE_VERSION: ${{ steps.vars.outputs.tag }}
run: |
echo $RELEASE_VERSION
echo ${{ steps.vars.outputs.tag }}
- uses: actions/checkout@v3
- name: Set up QEMU
id: quemu
uses: docker/setup-qemu-action@v2
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Collect images data
id: tags
run: |
echo '::set-output name=current::vegaprotocol/${{ matrix.app }}:${{ steps.vars.outputs.tag }}'
echo '::set-output name=latest::vegaprotocol/${{ matrix.app }}:latest'
- name: Echo tags
run: |
echo ${{ steps.tags.outputs.latest }}
echo ${{ steps.tags.outputs.current }}
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker build
uses: docker/build-push-action@v3
with:
context: '.'
file: './Dockerfile.next'
build-args: 'APP=${{ matrix.app }}'
platforms: ${{ matrix.build-platform }}
tags: ${{ steps.tags.outputs.latest }},${{ steps.tags.outputs.current }}
push: true
@@ -0,0 +1,60 @@
name: Publish CRA containers
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-pre[0-9]+'
jobs:
master:
strategy:
matrix:
build-platform: ['linux/amd64']
app: ['explorer', 'token']
name: Build the ${{ inputs.image_name }} image
runs-on: ubuntu-latest
steps:
- name: Set output
id: vars
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
- name: Check output
env:
RELEASE_VERSION: ${{ steps.vars.outputs.tag }}
run: |
echo $RELEASE_VERSION
echo ${{ steps.vars.outputs.tag }}
- uses: actions/checkout@v3
- name: Set up QEMU
id: quemu
uses: docker/setup-qemu-action@v2
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Collect images data
id: tags
run: |
echo '::set-output name=current::vegaprotocol/${{ matrix.app }}:${{ steps.vars.outputs.tag }}'
echo '::set-output name=latest::vegaprotocol/${{ matrix.app }}:latest'
- name: Echo tags
run: |
echo ${{ steps.tags.outputs.latest }}
echo ${{ steps.tags.outputs.current }}
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker build
uses: docker/build-push-action@v3
with:
context: '.'
file: './Dockerfile.cra'
build-args: 'APP=${{ matrix.app }}'
platforms: ${{ matrix.build-platform }}
tags: ${{ steps.tags.outputs.latest }},${{ steps.tags.outputs.current }}
push: true
@@ -1,126 +0,0 @@
name: Publish docker containers
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-pre[0-9]+'
workflow_dispatch:
inputs:
publish:
description: 'Publish tag to Docker Hub & GitHub Registry'
required: false
type: boolean
default: false
tag:
description: 'Git Tag to build and publish'
required: false
type: string
default: ''
apps:
description: 'Applications to build and publish'
required: false
type: choice
options:
- '["explorer", "token", "trading"]'
- '["explorer"]'
- '["token"]'
- '["trading"]'
archs:
description: 'Architecture to build and publish'
required: false
type: choice
options:
- linux/amd64, linux/arm64
- linux/amd64
- linux/arm64
jobs:
master:
strategy:
fail-fast: false
matrix:
app: ${{ fromJson(inputs.apps || '["explorer", "token", "trading"]') }}
name: Build the ${{ matrix.app }} image
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
with:
ref: ${{ inputs.tag }}
- name: Set up QEMU
id: quemu
uses: docker/setup-qemu-action@v2
- name: Available platforms
run: echo ${{ steps.qemu.outputs.platforms }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
if: ${{ inputs.publish || startsWith(github.ref, 'refs/tags/') }}
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Determine Docker Image tag
id: tags
run: |
hash=$(git rev-parse HEAD|cut -b1-8)
versionTag=${{ inputs.tag || startsWith(github.ref, 'refs/tags/') && github.ref_name || '${hash}' }}
echo ::set-output name=version::${versionTag}
echo ::set-output name=npmVersion::$(cat dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }} | grep FROM | head -n 1 | awk '{print $2}' | cut -d ':' -f 2 | cut -d '-' -f 1 )
- name: Print config
run: |
git rev-parse --verify HEAD
git status
echo "inputs.tag=${{ inputs.tag }}"
echo "inputs.publish=${{ inputs.publish }}"
echo "inputs.apps=${{ inputs.apps }}"
echo "inputs.archs=${{ inputs.archs }}"
echo "steps.tags.outputs.version=${{ steps.tags.outputs.version }}"
- uses: actions/setup-node@v3
with:
node-version: ${{ steps.tags.outputs.npmVersion }}
- name: Build frontend dists
run: |
yarn --verbose --pure-lockfile
yarn nx ${{ matrix.app =='trading' && 'export' || 'build' }} ${{ matrix.app }} --pure-lockfile
- name: Build and export to local Docker
uses: docker/build-push-action@v3
with:
context: .
push: false
file: dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }}.dist
build-args: APP=${{ matrix.app }}
load: true
tags: vegaprotocol/${{ matrix.app }}:local
- name: Sanity check docker image
run: |
docker run --rm vegaprotocol/${{ matrix.app }}:local cat .env
docker run --rm vegaprotocol/${{ matrix.app }}:local ls -lah
- name: Build and push to DockerHub
id: docker_build
uses: docker/build-push-action@v3
with:
context: .
push: ${{ inputs.publish || startsWith(github.ref, 'refs/tags/') }}
file: dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }}.dist
build-args: APP=${{ matrix.app }}
platforms: ${{ inputs.archs || 'linux/amd64, linux/arm64' }}
tags: |
vegaprotocol/${{ matrix.app }}:latest
vegaprotocol/${{ matrix.app }}:${{ steps.tags.outputs.version }}
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
+8 -14
View File
@@ -11,23 +11,20 @@ jobs:
master:
name: Test and lint - main
runs-on: ubuntu-latest
permissions:
contents: 'read'
actions: 'read'
if: ${{ github.event_name != 'pull_request' }}
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v3
uses: nrwl/nx-set-shas@v2
with:
main-branch-name: main
main-branch-name: master
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v3
uses: actions/setup-node@v2
with:
node-version: 16.14.0
- name: Install root dependencies
@@ -43,22 +40,19 @@ jobs:
pr:
name: Test and lint - PR
runs-on: ubuntu-latest
permissions:
contents: 'read'
actions: 'read'
if: ${{ github.event_name == 'pull_request' }}
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v3
uses: nrwl/nx-set-shas@v2
with:
main-branch-name: main
main-branch-name: master
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v3
uses: actions/setup-node@v2
with:
node-version: 16.14.0
- name: Install root dependencies
-3
View File
@@ -43,6 +43,3 @@ Thumbs.db
.local.env
.env.local
cypress.env.json
# Next.js
.next
+4
View File
@@ -6,3 +6,7 @@ yarn commitlint --edit "${1}"
# Lint all staged files
yarn lint-staged
# Build & test affected apps
yarn nx affected:build --uncommitted
yarn nx affected:test --uncommitted
-1
View File
@@ -3,7 +3,6 @@
/dist
/coverage
__generated__
__generated___
apps/static/src/assets/devnet-tranches.json
apps/static/src/assets/mainnet-tranches.json
@@ -8,8 +8,8 @@ COPY package.json ./
COPY yarn.lock ./
COPY . ./
RUN apk add python3 make gcc g++
RUN yarn --network-timeout 100000 --verbose --pure-lockfile
RUN yarn nx build $APP --network-timeout 100000 --verbose --pure-lockfile
RUN yarn
RUN yarn nx build $APP
# Production environment
FROM nginx:stable-alpine
@@ -8,8 +8,8 @@ COPY package.json ./
COPY yarn.lock ./
COPY . ./
RUN apk add python3 make gcc g++
RUN yarn --network-timeout 100000 --verbose --pure-lockfile
RUN yarn nx export $APP --network-timeout 100000 --verbose --pure-lockfile
RUN yarn
RUN yarn nx export $APP
# Production environment
FROM nginx:stable-alpine
+4 -17
View File
@@ -69,10 +69,10 @@ Run `nx serve my-app` for a dev server. Navigate to the port specified in `app/<
### Using Apollo GraphQL and Generate Types
In order to generate the schemas for your GraphQL queries, you can run `GRAPHQL_SCHEMA_PATH=[YOUR SCHEMA FILE / API URL HERE] nx run types:generate`.
In order to generate the schemas for your GraphQL queries, you can run `NX_VEGA_URL=[YOUR URL HERE] nx run types:generate`.
```bash
export GRAPHQL_SCHEMA_PATH=https://api.n11.testnet.vega.xyz/graphql
export NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
yarn nx run types:generate
```
@@ -106,12 +106,12 @@ Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more
## Docker
The [Dockerfile](./dockerfiles) for running the frontends is pretty basic, merely building the application with the APP arg that is passed in and serving the application from [nginx](./nginx/nginx.conf). The only complexity that exists is that there is a script which allows the passing of run time environment variables to the containers. See configuration below for how to do this.
The [Dockerfile](./Dockerfile) for running the frontends is pretty basic, merely building the application with the APP arg that is passed in and serving the application from [nginx](./nginx/nginx.conf). The only complexity that exists is that there is a script which allows the passing of run time environment variables to the containers. See configuration below for how to do this.
You can build any of the containers locally with the following command:
```bash
docker build --dockerfile dockerfiles/Dockerfile.cra . --build-arg APP=[YOUR APP] --tag=[TAG]
docker build . --build-arg APP=[YOUR APP] --tag=[TAG]
```
In order to run a container:
@@ -120,8 +120,6 @@ In order to run a container:
docker run -p 3000:80 [TAG]
```
Images ending with `.dist` are to pack locally created transpiled HTML files into nginx container for non-compatible with yarn architectures like M1 Mac
## Config
As environment variables are build time and not run time in frontend applications. We have built a system which allows for passing run time environment variables, this generates a JSON file that will override the default environment variables that the container was built with (which is always testnet, using the default .env files).
@@ -140,17 +138,6 @@ Coming soon! You will be able to run the containers within Vega Capsule.
You can run against a local instance of Vega Capsule today by using the .env.capsule present in the apps.
If you wish to run E2E tests for Token and Block Explorer (other areas to be added soon)
- Vegacapsule must be used in order for these tests to succeed, the vegacapsule repo README.md file contains the steps required to set this up, it must be installed globally.
- However we start the capsule network a little differently to how it is laid out in those instructions:
In order to run the bootstrap command to generate and start a new network, we must do so using the following:
```bash
vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl
```
# 📑 License
[MIT](./LICENSE)
+1 -8
View File
@@ -1,5 +1,5 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"root": "apps/console-lite-e2e",
"sourceRoot": "apps/console-lite-e2e/src",
"projectType": "application",
"targets": {
@@ -21,13 +21,6 @@
"options": {
"lintFilePatterns": ["apps/console-lite-e2e/**/*.{js,ts}"]
}
},
"build": {
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"command": "yarn tsc --project ./apps/console-lite-e2e/"
}
}
},
"tags": [],
@@ -95,36 +95,36 @@ describe('market list', () => {
});
describe('long list of results should be handled properly', () => {
it('handles 1000 markets', () => {
it('handles 5000 markets', () => {
cy.viewport(1440, 900);
cy.mockGQL((req) => {
aliasQuery(req, 'SimpleMarkets', generateLongListMarkets(1000));
aliasQuery(req, 'SimpleMarkets', generateLongListMarkets(5000));
});
performance.mark('start-1k');
performance.mark('start-5k');
cy.visit('/markets');
cy.get('.ag-center-cols-container', { timeout: 50000 }).then(() => {
performance.mark('end-1k');
performance.measure('load-1k', 'start-1k', 'end-1k');
const measure = performance.getEntriesByName('load-1k')[0];
performance.mark('end-5k');
performance.measure('load-5k', 'start-5k', 'end-5k');
const measure = performance.getEntriesByName('load-5k')[0];
expect(measure.duration).lte(20000);
cy.log(`Ag-grid 1k load took ${measure.duration} milliseconds.`);
cy.log(`Ag-grid 5k load took ${measure.duration} milliseconds.`);
cy.get('.ag-root').should('have.attr', 'aria-rowcount', '1001');
cy.get('.ag-root').should('have.attr', 'aria-rowcount', '5001');
cy.get('.ag-center-cols-container')
.find('[role="row"]')
.its('length')
.then((length) => expect(length).to.be.closeTo(20, 3));
.then((length) => expect(length).to.be.closeTo(21, 2));
cy.get('.ag-cell-label-container').eq(4).click();
cy.get('body').then(($body) => {
for (let i = 0; i < 15; i++) {
cy.wrap($body).realPress('Tab', { pressDelay: 100 });
for (let i = 0; i < 20; i++) {
cy.wrap($body).realPress('Tab', { pressDelay: 300 });
}
});
cy.focused().parent('.ag-row').should('have.attr', 'row-index', '14');
cy.focused().parent('.ag-row').should('have.attr', 'row-index', '19');
cy.get('.ag-center-cols-container')
.find('[role="row"]')
.its('length')
.then((length) => expect(length).to.be.closeTo(26, 2));
.then((length) => expect(length).to.be.closeTo(31, 2));
});
});
});
@@ -39,7 +39,7 @@ describe('market selector', () => {
connectVegaWallet();
cy.get('input[placeholder="Search"]').should(
'have.value',
markets[0].tradableInstrument.instrument.name
markets[0].name
);
cy.getByTestId('arrow-button').click();
cy.getByTestId('market-pane').should('be.visible');
@@ -47,7 +47,7 @@ describe('market selector', () => {
.children()
.find('[role="button"]')
.first()
.should('contain.text', markets[0].tradableInstrument.instrument.name);
.should('contain.text', markets[0].name);
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
@@ -70,8 +70,8 @@ describe('market selector', () => {
cy.get('input[placeholder="Search"]').type('aa');
const filtered = markets.filter(
(market) =>
market.state === 'STATE_ACTIVE' &&
market.tradableInstrument.instrument.name.match(/aa/i)
market.data.market.state === 'STATE_ACTIVE' &&
market.name.match(/aa/i)
);
cy.getByTestId('market-pane')
.children()
@@ -88,21 +88,21 @@ describe('market selector', () => {
);
cy.get('input[placeholder="Search"]').should(
'have.value',
filtered[filtered.length - 1].tradableInstrument.instrument.name
filtered[filtered.length - 1].name
);
}
});
// constantly failing on ci
it.skip('keyboard navigation should work well', () => {
it('keyboard navigation should work well', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[0].id}`);
connectVegaWallet();
cy.get('input[placeholder="Search"]').type('{backspace}');
cy.get('input[placeholder="Search"]').clear();
cy.focused().realPress('ArrowDown');
cy.focused().should('contain.text', 'AAVEDAI Monthly');
cy.focused().realPress('ArrowDown');
cy.focused().should('contain.text', 'ETHBTC').realPress('Enter');
cy.get('body').realPress('ArrowDown');
cy.focused().eq(0).should('contain.text', 'AAVEDAI Monthly');
cy.get('body').realPress('ArrowDown');
cy.focused().eq(0).should('contain.text', 'ETHBTC').realPress('Enter');
cy.location('pathname').should('eq', '/trading/ethbtc-quaterly');
cy.get('input[placeholder="Search"]').type('{backspace}');
@@ -127,11 +127,11 @@ describe('market selector', () => {
.children()
.find('[role="button"]')
.should('have.length', 3);
cy.get('div[role="dialog"]').should('have.class', 'w-screen');
cy.get('div[role="dialog"]').should('have.class', 'w-full');
cy.getByTestId('dialog-close').click();
cy.get('input[placeholder="Search"]').should(
'have.value',
markets[0].tradableInstrument.instrument.name
markets[0].name
);
}
});
@@ -7,7 +7,6 @@ import { generateEstimateOrder } from '../support/mocks/generate-estimate-order'
import { generatePartyBalance } from '../support/mocks/generate-party-balance';
import { generatePartyMarketData } from '../support/mocks/generate-party-market-data';
import { generateMarketMarkPrice } from '../support/mocks/generate-market-mark-price';
import { generateMarketDepth } from '../support/mocks/generate-market-depth';
import { connectVegaWallet } from '../support/connect-wallet';
describe('Market trade', () => {
@@ -22,7 +21,6 @@ describe('Market trade', () => {
aliasQuery(req, 'PartyBalanceQuery', generatePartyBalance());
aliasQuery(req, 'PartyMarketData', generatePartyMarketData());
aliasQuery(req, 'MarketMarkPrice', generateMarketMarkPrice());
aliasQuery(req, 'MarketDepth', generateMarketDepth());
});
cy.visit('/markets');
cy.wait('@SimpleMarkets').then((response) => {
@@ -169,51 +167,6 @@ describe('Market trade', () => {
.eq(0)
.find('button')
.should('have.text', '2');
cy.get('button').contains('Max').click();
}
});
it('slippage value should be displayed', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('button').contains('Max').click();
cy.get('#step-2-panel')
.find('dl')
.eq(2)
.find('dd')
.should('have.text', '0.02%');
}
});
it('allow slippage value to be adjusted', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('button').contains('Max').click();
cy.get('#step-2-panel')
.find('dl')
.eq(2)
.find('dd')
.should('have.text', '0.02%');
cy.get('#step-2-panel').find('dl').eq(2).find('button').click();
cy.get('#input-order-slippage')
.focus()
.type('{backspace}{backspace}{backspace}1');
cy.getByTestId('slippage-dialog').find('button').click();
cy.get('#step-2-panel')
.find('dl')
.eq(2)
.find('dd')
.should('have.text', '1%');
}
});
@@ -302,19 +255,17 @@ describe('Market trade', () => {
connectVegaWallet();
cy.get('#step-3-control').click();
// Start from the bottom tooltip to ensure the tooltip above
// can be interacted with
cy.getByTestId('review-trade').get('div.cursor-help').eq(1).realTouch();
cy.get('[data-radix-popper-content-wrapper]').contains(
'The notional size represents the position size'
);
cy.getByTestId('review-trade')
.get('#contracts_tooltip_trigger')
.realTouch();
cy.get('[data-radix-popper-content-wrapper]').contains(
'The number of contracts determines'
);
cy.getByTestId('review-trade').get('div.cursor-help').eq(1).realTouch();
cy.get('[data-radix-popper-content-wrapper]').contains(
'The notional size represents the position size'
);
}
});
});
@@ -30,8 +30,5 @@ describe('Portfolio page', () => {
cy.getByTestId('Fills').click();
cy.getByTestId('tab-fills').should('exist');
cy.getByTestId('Deposits').click();
cy.getByTestId('tab-deposits').should('exist');
});
});
@@ -75,11 +75,18 @@ const protoCandles = [
export const protoMarket = {
id: 'first-btcusd-id',
state: 'STATE_ACTIVE',
name: 'AAVEDAI Monthly (30 Jun 2022)',
data: {
market: {
id: 'first-btcusd-id',
state: 'STATE_ACTIVE',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'AAVEDAI.MF21',
name: 'AAVEDAI Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:2839D9B2329C9E70',
@@ -2,13 +2,13 @@ export const generateDealTicket = () => {
return {
market: {
id: 'first-btcusd-id',
name: 'AAVEDAI Monthly (30 Jun 2022)',
decimalPlaces: 5,
positionDecimalPlaces: 0,
state: 'STATE_ACTIVE',
tradingMode: 'Continuous',
tradableInstrument: {
instrument: {
name: 'AAVEDAI Monthly (30 Jun 2022)',
product: {
quoteName: 'DAI',
settlementAsset: {
@@ -1,218 +0,0 @@
export const generateMarketDepth = () => {
return {
market: {
id: 'a46bd7e5277087723b7ab835844dec3cef8b4445738101269624bf5537d5d423',
decimalPlaces: 5,
positionDecimalPlaces: 0,
data: {
staticMidPrice: '9893006',
marketTradingMode: 'TRADING_MODE_CONTINUOUS',
indicativeVolume: '0',
indicativePrice: '0',
bestStaticBidPrice: '9893006',
bestStaticOfferPrice: '9893006',
market: {
id: 'a46bd7e5277087723b7ab835844dec3cef8b4445738101269624bf5537d5d423',
__typename: 'Market',
},
__typename: 'MarketData',
},
depth: {
lastTrade: { price: '9893006', __typename: 'Trade' },
sell: [
{
price: '9893007',
volume: '3',
numberOfOrders: '3',
__typename: 'PriceLevel',
},
{
price: '9893010',
volume: '4',
numberOfOrders: '4',
__typename: 'PriceLevel',
},
{
price: '9893012',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893015',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893017',
volume: '2',
numberOfOrders: '2',
__typename: 'PriceLevel',
},
{
price: '9893021',
volume: '4',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893025',
volume: '5',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893125',
volume: '4',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893135',
volume: '2',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893165',
volume: '5',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893175',
volume: '3',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893185',
volume: '3',
numberOfOrders: '3',
__typename: 'PriceLevel',
},
{
price: '9894185',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9894585',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9895585',
volume: '4',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9896585',
volume: '2',
numberOfOrders: '2',
__typename: 'PriceLevel',
},
],
buy: [
{
price: '9893005',
volume: '4',
numberOfOrders: '3',
__typename: 'PriceLevel',
},
{
price: '9893003',
volume: '2',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9893001',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9892006',
volume: '3',
numberOfOrders: '2',
__typename: 'PriceLevel',
},
{
price: '9891006',
volume: '2',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9891001',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890101',
volume: '2',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890091',
volume: '5',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890081',
volume: '4',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890050',
volume: '2',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890040',
volume: '6',
numberOfOrders: '3',
__typename: 'PriceLevel',
},
{
price: '9890030',
volume: '6',
numberOfOrders: '2',
__typename: 'PriceLevel',
},
{
price: '9890021',
volume: '3',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890011',
volume: '1',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
{
price: '9890001',
volume: '11',
numberOfOrders: '1',
__typename: 'PriceLevel',
},
],
sequenceNumber: '1661773865550746910',
__typename: 'MarketDepth',
},
__typename: 'Market',
},
};
};
@@ -6,11 +6,11 @@ export const generateMarketNames = () => {
{ ...protoMarket },
{
id: '1d7ddf67dac4924db03f5bf58571a7bcb1908d70c66580467717aabc5345b68a',
name: 'Apple Monthly (30 Jun 2022)',
state: 'STATE_SUSPENDED',
tradableInstrument: {
instrument: {
code: 'AAPL.MF21',
name: 'Apple Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:4899E01009F1A721',
@@ -32,11 +32,11 @@ export const generateMarketNames = () => {
},
{
id: '87ae87cd3244fc1fab4b0e2dad2437879864192bb969f3109b69293421644c8b',
name: 'Tesla Quarterly (30 Jun 2022)',
state: 'STATE_SUSPENDED',
tradableInstrument: {
instrument: {
code: 'TSLA.QM21',
name: 'Tesla Quarterly (30 Jun 2022)',
metadata: {
tags: [
'formerly:5A86B190C384997F',
@@ -58,11 +58,11 @@ export const generateMarketNames = () => {
},
{
id: '69205712a854f1bbfb69fa3d11b60e01a1e249bafb5ece88115e7451e8ef07b3',
name: 'BTCUSD Monthly (30 Jun 2022)',
state: 'STATE_SUSPENDED',
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
@@ -83,11 +83,11 @@ export const generateMarketNames = () => {
},
{
id: 'ethbtc-quaterly',
name: 'ETHBTC Quarterly (30 Jun 2022)',
state: 'STATE_ACTIVE',
tradableInstrument: {
instrument: {
code: 'ETHBTC.QM21',
name: 'ETHBTC Quarterly (30 Jun 2022)',
metadata: {
tags: [
'formerly:1F0BB6EB5703B099',
@@ -108,11 +108,11 @@ export const generateMarketNames = () => {
},
{
id: '3c62b2714c4332d1a689a5352bff090e6aabccfd6bd87ce018936b38372530c9',
name: 'UNIDAI Monthly (30 Jun 2022)',
state: 'STATE_ACTIVE',
tradableInstrument: {
instrument: {
code: 'UNIDAI.MF21',
name: 'UNIDAI Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:3C58ED2A4A6C5D7E',
@@ -6,11 +6,19 @@ export const generateSimpleMarkets = () => {
{ ...protoMarket },
{
id: '57fbaa322e97cfc8bb5f1de048c37e033c41b1ac1906d3aed9960912a067ef5a',
name: 'CELUSD (June 2022)',
state: 'Active',
data: {
market: {
id: '57fbaa322e97cfc8bb5f1de048c37e033c41b1ac1906d3aed9960912a067ef5a',
state: 'STATE_PENDING',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'CELUSD',
name: 'CELUSD (June 2022)',
metadata: {
tags: [
'base:CEL',
@@ -35,11 +43,19 @@ export const generateSimpleMarkets = () => {
},
{
id: 'ccf2f04865e5951ac3405da6e16b7cbdb535a0ad32df4df2dbed4262cf473255',
name: 'XMRUSD market',
state: 'Suspended',
data: {
market: {
id: 'ccf2f04865e5951ac3405da6e16b7cbdb535a0ad32df4df2dbed4262cf473255',
state: 'STATE_PROPOSED',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'XMRUSD',
name: 'XMRUSD market',
metadata: {
tags: [
'base:monero',
@@ -64,11 +80,19 @@ export const generateSimpleMarkets = () => {
},
{
id: '6030e5b4e0ca3297a26081e5af4d453f97f96baab2d74bf56f84efcffc4c382f',
name: 'UNIDAI Monthly (30 Jun 2022)',
state: 'Active',
data: {
market: {
id: '6030e5b4e0ca3297a26081e5af4d453f97f96baab2d74bf56f84efcffc4c382f',
state: 'STATE_SUSPENDED',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'UNIDAI.MF21',
name: 'UNIDAI Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:3C58ED2A4A6C5D7E',
@@ -167,11 +191,19 @@ export const generateSimpleMarkets = () => {
},
{
id: 'de74a5572045b300e8ec50d136896912ec76e7d7ec135bc305dfd4854d9363a4',
name: 'XMRUSD market',
state: 'Pending',
data: {
market: {
id: 'de74a5572045b300e8ec50d136896912ec76e7d7ec135bc305dfd4854d9363a4',
state: 'STATE_ACTIVE',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'XMRUSD',
name: 'XMRUSD market',
metadata: {
tags: [
'base:monero',
@@ -269,11 +301,19 @@ export const generateSimpleMarkets = () => {
},
{
id: '31ea96284611771e486c820acb26a325a99664f9854b5a7e7ad99023efa8f9e6',
name: 'ApeCoin (18 Jul 2022)',
state: 'Closed',
data: {
market: {
id: '31ea96284611771e486c820acb26a325a99664f9854b5a7e7ad99023efa8f9e6',
state: 'STATE_PROPOSED',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'APE/USD',
name: 'ApeCoin (18 Jul 2022)',
metadata: {
tags: [
'quote:USD',
@@ -297,11 +337,19 @@ export const generateSimpleMarkets = () => {
},
{
id: '34cff959cdc2ffdb0f167820d701fe8b51cc6b8588e650d93369aaa22d6f8b74',
name: 'BTCUSD Monthly (18 Jul 2022)',
state: 'Settled',
data: {
market: {
id: '34cff959cdc2ffdb0f167820d701fe8b51cc6b8588e650d93369aaa22d6f8b74',
state: 'STATE_PROPOSED',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (18 Jul 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
@@ -327,11 +375,19 @@ export const generateSimpleMarkets = () => {
},
{
id: '87b0bbb3c171baa5d97dfc3852332829c91e5c5dc9f7c8fb584c6d8ac75aaaf2',
name: 'Builders Club x ETHcc',
state: 'Settled',
data: {
market: {
id: '87b0bbb3c171baa5d97dfc3852332829c91e5c5dc9f7c8fb584c6d8ac75aaaf2',
state: 'STATE_ACTIVE',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'LTCUSD',
name: 'Builders Club x ETHcc',
metadata: {
tags: [
'base:litecoin',
@@ -417,11 +473,19 @@ export const generateSimpleMarkets = () => {
},
{
id: '45266143c6e9b58f4cff9b8906e971c531bb29ea7af01066973f9b77e8134823',
name: 'BTCUSD Monthly (18 Jul 2022)',
state: 'Pending',
data: {
market: {
id: '45266143c6e9b58f4cff9b8906e971c531bb29ea7af01066973f9b77e8134823',
state: 'STATE_PROPOSED',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (18 Jul 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
@@ -447,11 +511,19 @@ export const generateSimpleMarkets = () => {
},
{
id: '65663ebdc96161162769c4d5c5508137416748178d7cb28e2cb0d07a151a2bc6',
name: 'ETHBTC Quarterly (30 Jun 2022)',
state: 'Active',
data: {
market: {
id: '65663ebdc96161162769c4d5c5508137416748178d7cb28e2cb0d07a151a2bc6',
state: 'STATE_ACTIVE',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'ETHBTC.QM21',
name: 'ETHBTC Quarterly (30 Jun 2022)',
metadata: {
tags: [
'formerly:1F0BB6EB5703B099',
@@ -550,11 +622,19 @@ export const generateSimpleMarkets = () => {
},
{
id: '234afabd27e3bce1a879039c041f9f00f915d98459935ddafd0169d38dd13850',
name: 'Apple Monthly (30 Jun 2022)',
state: 'Active',
data: {
market: {
id: '234afabd27e3bce1a879039c041f9f00f915d98459935ddafd0169d38dd13850',
state: 'STATE_ACTIVE',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'AAPL.MF21',
name: 'Apple Monthly (30 Jun 2022)',
metadata: {
tags: [
'formerly:4899E01009F1A721',
@@ -654,11 +734,19 @@ export const generateSimpleMarkets = () => {
},
{
id: '9f2a3c1caa67bb0773ec18d908d32b55b129b9ec2d106a8e9a87f6aa5c0375a6',
name: 'Tesla Quarterly (30 Jun 2022)',
state: 'Active',
data: {
market: {
id: '9f2a3c1caa67bb0773ec18d908d32b55b129b9ec2d106a8e9a87f6aa5c0375a6',
state: 'STATE_ACTIVE',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'TSLA.QM21',
name: 'Tesla Quarterly (30 Jun 2022)',
metadata: {
tags: [
'formerly:5A86B190C384997F',
@@ -758,11 +846,19 @@ export const generateSimpleMarkets = () => {
},
{
id: '8a45ee934d3ddac4b036f9884df1064a5353c620a56f775ba36597d0edef9a7a',
name: 'Go big or Go home',
state: 'Active',
data: {
market: {
id: '8a45ee934d3ddac4b036f9884df1064a5353c620a56f775ba36597d0edef9a7a',
state: 'STATE_SUSPENDED',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'LTCUSD',
name: 'Go big or Go home',
metadata: {
tags: [
'base:litecoin',
@@ -801,11 +897,19 @@ export const generateSimpleMarkets = () => {
},
{
id: 'aede7b9ac0c3b225004929c5455160a00f59864aad32ec366e8a2bff1b30fd0f',
name: 'BTCUSD Monthly (08 Jul 2022)',
state: 'Active',
data: {
market: {
id: 'aede7b9ac0c3b225004929c5455160a00f59864aad32ec366e8a2bff1b30fd0f',
state: 'STATE_PROPOSED',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (08 Jul 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
@@ -831,11 +935,19 @@ export const generateSimpleMarkets = () => {
},
{
id: '9398707e01daa1a1f1ca6ff87cf8d6c03fe7373ce31121ce81b99a129e6bda47',
name: 'BTCUSD Monthly (18 Jul 2022)',
state: 'Active',
data: {
market: {
id: '9398707e01daa1a1f1ca6ff87cf8d6c03fe7373ce31121ce81b99a129e6bda47',
state: 'STATE_PROPOSED',
__typename: 'Market',
},
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (18 Jul 2022)',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
@@ -866,15 +978,14 @@ export const generateSimpleMarkets = () => {
export const generateLongListMarkets = (count: number) => {
const markets = [];
for (let i = 0; i < count; i++) {
const { id, name } = protoMarket;
markets.push({
...protoMarket,
id: protoMarket.id + i,
tradableInstrument: {
...protoMarket.tradableInstrument,
instrument: {
...protoMarket.tradableInstrument.instrument,
name: protoMarket.tradableInstrument.instrument.name + i,
},
id: id + i,
name: name + i,
data: {
...protoMarket.data,
id: id + i,
},
});
}
-2
View File
@@ -1,9 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"sourceMap": false,
"allowSyntheticDefaultImports": true,
"outDir": "../../dist/out-tsc",
"allowJs": true,
"types": ["cypress", "node", "cypress-real-events"]
+2 -2
View File
@@ -1,8 +1,8 @@
# App configuration variables
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mainnet-network.json
NX_VEGA_URL=https://api.vega.xyz/query
NX_VEGA_URL=https://api.token.vega.xyz/query
NX_VEGA_ENV=MAINNET
NX_VEGA_REST=https://api.vega.xyz
NX_VEGA_REST=https://api.token.vega.xyz
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
+1 -1
View File
@@ -1,6 +1,6 @@
{
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
"ignorePatterns": ["!**/*", "__generated__"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
@@ -1,5 +1,4 @@
/* eslint-disable */
export default {
module.exports = {
displayName: 'console-lite',
preset: '../../jest.preset.js',
transform: {
+4 -4
View File
@@ -1,5 +1,5 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"root": "apps/console-lite",
"sourceRoot": "apps/console-lite/src",
"projectType": "application",
"targets": {
@@ -43,7 +43,7 @@
"serve": {
"executor": "./tools/executors/webpack:serve",
"options": {
"buildTarget": "console-lite:build:development",
"buildTarget": "console-lite:build",
"hmr": true,
"port": 4001
},
@@ -65,12 +65,12 @@
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/apps/console-lite"],
"options": {
"jestConfig": "apps/console-lite/jest.config.ts",
"jestConfig": "apps/console-lite/jest.config.js",
"passWithNoTests": true
}
},
"build-netlify": {
"executor": "@nrwl/workspace:run-commands",
"builder": "@nrwl/workspace:run-commands",
"options": {
"commands": [
"cp apps/console-lite/netlify.toml netlify.toml",
-22
View File
@@ -1,29 +1,7 @@
import '@testing-library/jest-dom';
import { defaultFallbackInView } from 'react-intersection-observer';
import ResizeObserver from 'resize-observer-polyfill';
defaultFallbackInView(true);
global.ResizeObserver = ResizeObserver;
global.DOMRect = class DOMRect {
bottom = 0;
left = 0;
right = 0;
top = 0;
constructor(
public x = 0,
public y = 0,
public width = 0,
public height = 0
) {}
static fromRect(other?: DOMRectInit): DOMRect {
return new DOMRect(other?.x, other?.y, other?.width, other?.height);
}
toJSON() {
return JSON.stringify(this);
}
};
Object.defineProperty(window, 'matchMedia', {
writable: true,
+2 -2
View File
@@ -19,7 +19,7 @@ import useLocalValues from './hooks/use-local-values';
function App() {
const [theme, toggleTheme] = useThemeSwitcher();
const localValues = useLocalValues(theme, toggleTheme);
const localValues = useLocalValues(toggleTheme);
const {
vegaWalletDialog,
menu: { setMenuOpen },
@@ -37,7 +37,7 @@ function App() {
<VegaWalletProvider>
<LocalContext.Provider value={localValues}>
<AppLoader>
<div className="max-h-full min-h-full dark:bg-lite-black dark:text-neutral-200 bg-white text-neutral-800 grid grid-rows-[min-content,1fr]">
<div className="max-h-full min-h-full dark:bg-lite-black dark:text-white-60 bg-white text-black-60 grid grid-rows-[min-content,1fr]">
<Header />
<Main />
<VegaConnectDialog
@@ -1,11 +0,0 @@
query MarketTags($marketId: ID!) {
market(id: $marketId) {
tradableInstrument {
instrument {
metadata {
tags
}
}
}
}
}
@@ -1,14 +0,0 @@
query PartyBalanceQuery($partyId: ID!) {
party(id: $partyId) {
accounts {
type
balance
asset {
id
symbol
name
decimals
}
}
}
}
@@ -26,7 +26,7 @@ export interface MarketTags_market_tradableInstrument_instrument {
export interface MarketTags_market_tradableInstrument {
__typename: "TradableInstrument";
/**
* An instance of or reference to a fully specified instrument.
* An instance of, or reference to, a fully specified instrument.
*/
instrument: MarketTags_market_tradableInstrument_instrument;
}
@@ -34,14 +34,14 @@ export interface MarketTags_market_tradableInstrument {
export interface MarketTags_market {
__typename: "Market";
/**
* An instance of or reference to a tradable instrument.
* An instance of, or reference to, a tradable instrument.
*/
tradableInstrument: MarketTags_market_tradableInstrument;
}
export interface MarketTags {
/**
* An instrument that is trading on the VEGA network
* An instrument that is trading on the Vega network
*/
market: MarketTags_market | null;
}
@@ -3,7 +3,7 @@
// @generated
// This file was automatically generated and should not be edited.
import { AccountType } from "./../../../../../../../libs/types/src/__generated__/globalTypes";
import { AccountType } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: PartyBalanceQuery
@@ -12,7 +12,7 @@ import { AccountType } from "./../../../../../../../libs/types/src/__generated__
export interface PartyBalanceQuery_party_accounts_asset {
__typename: "Asset";
/**
* The id of the asset
* The ID of the asset
*/
id: string;
/**
@@ -24,7 +24,7 @@ export interface PartyBalanceQuery_party_accounts_asset {
*/
name: string;
/**
* The precision of the asset
* The precision of the asset. Should match the decimal precision of the asset on its native chain, e.g: for ERC20 assets, it is often 18
*/
decimals: number;
}
@@ -55,7 +55,7 @@ export interface PartyBalanceQuery_party {
export interface PartyBalanceQuery {
/**
* An entity that is trading on the VEGA network
* An entity that is trading on the Vega network
*/
party: PartyBalanceQuery_party | null;
}
@@ -1,54 +0,0 @@
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type MarketTagsQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type MarketTagsQuery = { __typename?: 'Query', market?: { __typename?: 'Market', tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null } } } } | null };
export const MarketTagsDocument = gql`
query MarketTags($marketId: ID!) {
market(id: $marketId) {
tradableInstrument {
instrument {
metadata {
tags
}
}
}
}
}
`;
/**
* __useMarketTagsQuery__
*
* To run a query within a React component, call `useMarketTagsQuery` and pass it any options that fit your needs.
* When your component renders, `useMarketTagsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useMarketTagsQuery({
* variables: {
* marketId: // value for 'marketId'
* },
* });
*/
export function useMarketTagsQuery(baseOptions: Apollo.QueryHookOptions<MarketTagsQuery, MarketTagsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<MarketTagsQuery, MarketTagsQueryVariables>(MarketTagsDocument, options);
}
export function useMarketTagsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketTagsQuery, MarketTagsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<MarketTagsQuery, MarketTagsQueryVariables>(MarketTagsDocument, options);
}
export type MarketTagsQueryHookResult = ReturnType<typeof useMarketTagsQuery>;
export type MarketTagsLazyQueryHookResult = ReturnType<typeof useMarketTagsLazyQuery>;
export type MarketTagsQueryResult = Apollo.QueryResult<MarketTagsQuery, MarketTagsQueryVariables>;
@@ -1,57 +0,0 @@
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type PartyBalanceQueryQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type PartyBalanceQueryQuery = { __typename?: 'Query', party?: { __typename?: 'Party', accounts?: Array<{ __typename?: 'Account', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number } }> | null } | null };
export const PartyBalanceQueryDocument = gql`
query PartyBalanceQuery($partyId: ID!) {
party(id: $partyId) {
accounts {
type
balance
asset {
id
symbol
name
decimals
}
}
}
}
`;
/**
* __usePartyBalanceQueryQuery__
*
* To run a query within a React component, call `usePartyBalanceQueryQuery` and pass it any options that fit your needs.
* When your component renders, `usePartyBalanceQueryQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = usePartyBalanceQueryQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function usePartyBalanceQueryQuery(baseOptions: Apollo.QueryHookOptions<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>(PartyBalanceQueryDocument, options);
}
export function usePartyBalanceQueryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>(PartyBalanceQueryDocument, options);
}
export type PartyBalanceQueryQueryHookResult = ReturnType<typeof usePartyBalanceQueryQuery>;
export type PartyBalanceQueryLazyQueryHookResult = ReturnType<typeof usePartyBalanceQueryLazyQuery>;
export type PartyBalanceQueryQueryResult = Apollo.QueryResult<PartyBalanceQueryQuery, PartyBalanceQueryQueryVariables>;
@@ -15,7 +15,3 @@ export const NOTIONAL_SIZE_TOOLTIP_TEXT = t(
export const EST_FEES_TOOLTIP_TEXT = t(
'When you execute a new buy or sell order, you must pay a small amount of commission to the network for doing so. This fee is used to provide income to the node operates of the network and market makers who make prices on the futures market you are trading.'
);
export const EST_SLIPPAGE = t(
'When you execute a trade on Vega, the price obtained in the market may differ from the best available price displayed at the time of placing the trade. The estimated slippage shows the difference between the best available price and the estimated execution price, determined by market liquidity and your chosen order size.'
);
@@ -54,7 +54,7 @@ export const DealTicketContainer = () => {
{(data) => {
const balance = (
<DealTicketBalance
className="mb-4"
className="mb-16"
settlementAsset={
data.market.tradableInstrument.instrument.product?.settlementAsset
}
@@ -76,7 +76,7 @@ export const DealTicketContainer = () => {
);
return (
<section className="flex p-4 md:p-6">
<section className="flex">
<section className="w-full md:w-1/2 md:min-w-[500px]">
{keypair ? container : <ConnectWallet />}
</section>
@@ -4,7 +4,6 @@ import { t } from '@vegaprotocol/react-helpers';
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
import { IconNames } from '@blueprintjs/icons';
import * as constants from './constants';
import { TrafficLight } from '../traffic-light';
interface DealTicketEstimatesProps {
quoteName?: string;
@@ -14,7 +13,6 @@ interface DealTicketEstimatesProps {
fees?: string;
notionalSize?: string;
size?: string;
slippage?: string;
}
interface DataTitleProps {
@@ -22,7 +20,7 @@ interface DataTitleProps {
quoteName?: string;
}
export const DataTitle = ({ children, quoteName = '' }: DataTitleProps) => (
const DataTitle = ({ children, quoteName = '' }: DataTitleProps) => (
<dt>
{children}
{quoteName && <small> ({quoteName})</small>}
@@ -30,20 +28,14 @@ export const DataTitle = ({ children, quoteName = '' }: DataTitleProps) => (
);
interface ValueTooltipProps {
value?: string;
children?: ReactNode;
value: string;
description: string;
id?: string;
}
export const ValueTooltipRow = ({
value,
children,
description,
id,
}: ValueTooltipProps) => (
<dd className="flex gap-x-2 items-center">
{value || children}
const ValueTooltipRow = ({ value, description, id }: ValueTooltipProps) => (
<dd className="flex gap-x-5 items-center">
{value}
<Tooltip align="center" description={description}>
<div className="cursor-help" id={id || ''} tabIndex={-1}>
<Icon
@@ -64,11 +56,10 @@ export const DealTicketEstimates = ({
fees,
notionalSize,
size,
slippage,
}: DealTicketEstimatesProps) => (
<dl className="text-black dark:text-white">
{size && (
<div className="flex justify-between mb-2">
<div className="flex justify-between mb-8">
<DataTitle>{t('Contracts')}</DataTitle>
<ValueTooltipRow
value={size}
@@ -78,13 +69,13 @@ export const DealTicketEstimates = ({
</div>
)}
{price && (
<div className="flex justify-between mb-2">
<div className="flex justify-between mb-8">
<DataTitle>{t('Est. Price')}</DataTitle>
<dd>{price}</dd>
</div>
)}
{notionalSize && (
<div className="flex justify-between mb-2">
<div className="flex justify-between mb-8">
<DataTitle quoteName={quoteName}>{t('Est. Position Size')}</DataTitle>
<ValueTooltipRow
value={notionalSize}
@@ -93,7 +84,7 @@ export const DealTicketEstimates = ({
</div>
)}
{fees && (
<div className="flex justify-between mb-2">
<div className="flex justify-between mb-8">
<DataTitle quoteName={quoteName}>{t('Est. Fees')}</DataTitle>
<ValueTooltipRow
value={fees}
@@ -102,7 +93,7 @@ export const DealTicketEstimates = ({
</div>
)}
{estMargin && (
<div className="flex justify-between mb-2">
<div className="flex justify-between mb-8">
<DataTitle quoteName={quoteName}>{t('Est. Margin')}</DataTitle>
<ValueTooltipRow
value={estMargin}
@@ -111,23 +102,17 @@ export const DealTicketEstimates = ({
</div>
)}
{estCloseOut && (
<div className="flex justify-between mb-2">
<DataTitle quoteName={quoteName}>{t('Est. Close out')}</DataTitle>
<div className="flex justify-between">
<dt>
<span>{t('Est. Close out')}</span>
&nbsp;
<small>({quoteName})</small>
</dt>
<ValueTooltipRow
value={estCloseOut}
description={constants.EST_CLOSEOUT_TOOLTIP_TEXT}
/>
</div>
)}
{slippage && (
<div className="flex justify-between mb-2">
<DataTitle>{t('Est. Price Impact / Slippage')}</DataTitle>
<ValueTooltipRow description={constants.EST_SLIPPAGE}>
<TrafficLight value={parseFloat(slippage)} q1={1} q2={5}>
{slippage}%
</TrafficLight>
</ValueTooltipRow>
</div>
)}
</dl>
);
@@ -1,149 +0,0 @@
import React, { useCallback, useState } from 'react';
import { BigNumber } from 'bignumber.js';
import { t } from '@vegaprotocol/react-helpers';
import {
SliderRoot,
SliderThumb,
SliderTrack,
SliderRange,
FormGroup,
} from '@vegaprotocol/ui-toolkit';
import { InputSetter } from '../input-setter';
interface DealTicketSizeInputProps {
step: number;
min: number;
max: number;
value: number;
onValueChange: (value: number) => void;
positionDecimalPlaces: number;
}
const getSizeLabel = (value: number): string => {
const MIN_LABEL = 'Min';
const MAX_LABEL = 'Max';
if (value === 0) {
return MIN_LABEL;
} else if (value === 100) {
return MAX_LABEL;
}
return `${value}%`;
};
export const DealTicketSizeInput = ({
value,
step,
min,
max,
onValueChange,
positionDecimalPlaces,
}: DealTicketSizeInputProps) => {
const sizeRatios = [0, 25, 50, 75, 100];
const [inputValue, setInputValue] = useState(value);
const onInputValueChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
let value = parseFloat(event.target.value);
const isLessThanMin = value < min;
const isMoreThanMax = value > max;
if (isLessThanMin) {
value = min;
} else if (isMoreThanMax) {
value = max;
}
if (value) {
onValueChange(value);
}
setInputValue(value);
},
[min, max, onValueChange, setInputValue]
);
const onButtonValueChange = (size: number) => {
const newVal = new BigNumber(size)
.decimalPlaces(positionDecimalPlaces)
.toNumber();
onValueChange(newVal);
setInputValue(newVal);
};
const onSliderValueChange = useCallback(
(value: number[]) => {
const val = value[0];
setInputValue(val);
onValueChange(val);
},
[onValueChange]
);
return (
<div>
<div className="flex justify-between text-black dark:text-white mb-2">
<span data-testid="min-label">{min}</span>
<span data-testid="max-label">{max}</span>
</div>
<SliderRoot
className="mb-2"
value={[value]}
onValueChange={onSliderValueChange}
step={step}
min={min}
max={max}
>
<SliderTrack className="bg-lightGrey dark:bg-offBlack">
<SliderRange className="!bg-black dark:!bg-white" />
</SliderTrack>
<SliderThumb />
</SliderRoot>
<div
data-testid="percentage-selector"
className="flex w-full justify-between text-black dark:text-white mb-6"
>
{sizeRatios.map((size, index) => {
const proportionalSize = size ? (size / 100) * max : min;
return (
<button
className="no-underline hover:underline text-blue"
onClick={() => onButtonValueChange(proportionalSize)}
type="button"
key={index}
>
{getSizeLabel(size)}
</button>
);
})}
</div>
<dl className="text-black dark:text-white">
<div className="flex items-center justify-between">
<dt>{t('Contracts')}</dt>
<dd className="flex justify-end w-full">
<FormGroup
hideLabel={true}
label="Enter Size"
labelFor="trade-size-input"
className="mb-1"
>
<InputSetter
id="input-order-size-market"
type="number"
step={step}
min={min}
max={max}
className="w-full"
value={inputValue}
onChange={onInputValueChange}
>
{inputValue}
</InputSetter>
</FormGroup>
</dd>
</div>
</dl>
</div>
);
};
@@ -1,13 +1,23 @@
import React from 'react';
import React, { useCallback, useState } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import {
SliderRoot,
SliderThumb,
SliderTrack,
SliderRange,
Button,
Input,
FormGroup,
} from '@vegaprotocol/ui-toolkit';
import { BigNumber } from 'bignumber.js';
import { DealTicketEstimates } from './deal-ticket-estimates';
import { DealTicketSizeInput } from './deal-ticket-size-input';
interface DealTicketSizeProps {
step: number;
min: number;
max: number;
size: number;
onSizeChange: (value: number) => void;
value: number;
onValueChange: (value: number[]) => void;
name: string;
quoteName: string;
price: string;
@@ -18,31 +28,167 @@ interface DealTicketSizeProps {
notionalSize: string;
}
const getSizeLabel = (value: number): string => {
const MIN_LABEL = 'Min';
const MAX_LABEL = 'Max';
if (value === 0) {
return MIN_LABEL;
} else if (value === 100) {
return MAX_LABEL;
}
return `${value}%`;
};
export const DealTicketSize = ({
value,
step,
min,
max,
price,
quoteName,
size,
onSizeChange,
onValueChange,
estCloseOut,
positionDecimalPlaces,
fees,
notionalSize,
}: DealTicketSizeProps) => {
const sizeRatios = [0, 25, 50, 75, 100];
const [inputValue, setInputValue] = useState(value);
const [isInputVisible, setIsInputVisible] = useState(false);
const onInputValueChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const value = parseFloat(event.target.value);
const isLessThanMin = value < min;
const isMoreThanMax = value > max;
if (value) {
if (isLessThanMin) {
onValueChange([min]);
} else if (isMoreThanMax) {
onValueChange([max]);
} else {
onValueChange([value]);
}
}
setInputValue(value);
},
[min, max, onValueChange, setInputValue]
);
const onButtonValueChange = useCallback(
(size: number) => {
if (isInputVisible) {
setIsInputVisible(false);
}
const newVal = new BigNumber(size)
.decimalPlaces(positionDecimalPlaces)
.toNumber();
onValueChange([newVal]);
setInputValue(newVal);
},
[isInputVisible, onValueChange, positionDecimalPlaces]
);
const toggleInput = useCallback(() => {
setIsInputVisible(!isInputVisible);
}, [isInputVisible]);
const onInputEnter = useCallback(
(event: React.KeyboardEvent) => {
if (event.key === 'Enter') {
event.stopPropagation();
toggleInput();
}
},
[toggleInput]
);
return max === 0 ? (
<p>Not enough balance to trade</p>
) : (
<div>
<DealTicketSizeInput
<div className="flex justify-between text-black dark:text-white mb-8">
<span data-testid="min-label">{min}</span>
<span data-testid="max-label">{max}</span>
</div>
<SliderRoot
className="mb-8"
value={[value]}
onValueChange={onValueChange}
step={step}
min={min}
max={max}
value={size}
onValueChange={onSizeChange}
positionDecimalPlaces={positionDecimalPlaces}
/>
>
<SliderTrack className="bg-lightGrey dark:bg-offBlack">
<SliderRange className="!bg-black dark:!bg-white" />
</SliderTrack>
<SliderThumb />
</SliderRoot>
<div
data-testid="percentage-selector"
className="flex w-full justify-between text-black dark:text-white mb-32"
>
{sizeRatios.map((size, index) => {
const proportionalSize = size ? (size / 100) * max : min;
return (
<Button
variant="inline-link"
className="no-underline !text-blue"
onClick={() => onButtonValueChange(proportionalSize)}
key={index}
>
{getSizeLabel(size)}
</Button>
);
})}
</div>
<dl className="text-black dark:text-white">
<div className="flex items-center justify-between mb-8">
<dt>{t('Contracts')}</dt>
<dd className="flex justify-end w-full">
<FormGroup
className="mb-0 flex items-center"
labelClassName="mr-8 sr-only"
label="Enter Size"
labelFor="trade-size-input"
>
{isInputVisible ? (
<>
<Input
id="input-order-size-market"
type="number"
step={step}
min={min}
max={max}
className="w-full"
value={inputValue}
onKeyDown={onInputEnter}
onChange={onInputValueChange}
/>
<Button
variant="inline-link"
className="no-underline !text-blue"
onClick={toggleInput}
>
{t('set')}
</Button>
</>
) : (
<Button
variant="inline-link"
className="no-underline !text-blue"
onClick={toggleInput}
>
{value}
</Button>
)}
</FormGroup>
</dd>
</div>
</dl>
<DealTicketEstimates
quoteName={quoteName}
fees={fees}
@@ -1,104 +0,0 @@
import React, { useCallback, useState } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import * as constants from './constants';
import { TrafficLight } from '../traffic-light';
import { Dialog, Icon, Intent, Tooltip } from '@vegaprotocol/ui-toolkit';
import { InputSetter } from '../../components/input-setter';
import { IconNames } from '@blueprintjs/icons';
import { DataTitle, ValueTooltipRow } from './deal-ticket-estimates';
interface DealTicketSlippageProps {
step?: number;
min?: number;
max?: number;
value: number;
onValueChange(value: number): void;
}
export const DealTicketSlippage = ({
value,
step = 0.01,
min = 0,
max = 50,
onValueChange,
}: DealTicketSlippageProps) => {
const [isDialogVisible, setIsDialogVisible] = useState(false);
const onChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
const numericValue = parseFloat(value);
onValueChange(numericValue);
},
[onValueChange]
);
const toggleDialog = useCallback(() => {
setIsDialogVisible(!isDialogVisible);
}, [isDialogVisible]);
const formLabel = (
<label className="flex items-center mb-1">
<span className="mr-1">{t('Adjust slippage tolerance')}</span>
<Tooltip align="center" description={constants.EST_SLIPPAGE}>
<div className="cursor-help" tabIndex={-1}>
<Icon
name={IconNames.ISSUE}
className="block rotate-180"
ariaLabel={constants.EST_SLIPPAGE}
/>
</div>
</Tooltip>
</label>
);
return (
<>
<Dialog
open={isDialogVisible}
onChange={toggleDialog}
intent={Intent.None}
title={t('Transaction Settings')}
>
<div data-testid="slippage-dialog">
{formLabel}
<InputSetter
id="input-order-slippage"
isInputVisible
hasError={!value}
type="number"
step={step}
min={min}
max={max}
className="w-full"
value={value}
onChange={onChange}
>
{value}%
</InputSetter>
</div>
</Dialog>
<dl className="text-black dark:text-white">
<div className="flex justify-between mb-2">
<DataTitle>{t('Est. Price Impact / Slippage')}</DataTitle>
<div className="flex">
<div className="mr-1">
<ValueTooltipRow description={constants.EST_SLIPPAGE}>
<TrafficLight value={value} q1={1} q2={5}>
{value}%
</TrafficLight>
</ValueTooltipRow>
</div>
<button type="button" onClick={toggleDialog}>
<Icon
name={IconNames.COG}
className="block rotate-180"
ariaLabel={t('Override slippage value')}
/>
</button>
</div>
</div>
</dl>
</>
);
};
@@ -1,26 +1,23 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useForm, Controller } from 'react-hook-form';
import { Stepper } from '../stepper';
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
import { InputError } from '@vegaprotocol/ui-toolkit';
import { BigNumber } from 'bignumber.js';
import { MarketSelector } from '@vegaprotocol/deal-ticket';
import {
getOrderDialogTitle,
getOrderDialogIntent,
getOrderDialogIcon,
MarketSelector,
} from '@vegaprotocol/deal-ticket';
import type { Order } from '@vegaprotocol/orders';
import { useVegaWallet, VegaTxStatus } from '@vegaprotocol/wallet';
import {
t,
addDecimalsFormatNumber,
toDecimal,
removeDecimal,
} from '@vegaprotocol/react-helpers';
import { t, addDecimal, toDecimal } from '@vegaprotocol/react-helpers';
import {
getDefaultOrder,
useOrderValidation,
useOrderSubmit,
getOrderDialogTitle,
getOrderDialogIntent,
getOrderDialogIcon,
OrderFeedback,
validateSize,
} from '@vegaprotocol/orders';
@@ -32,9 +29,6 @@ import type { PartyBalanceQuery } from './__generated__/PartyBalanceQuery';
import useOrderCloseOut from '../../hooks/use-order-closeout';
import useOrderMargin from '../../hooks/use-order-margin';
import useMaximumPositionSize from '../../hooks/use-maximum-position-size';
import useCalculateSlippage from '../../hooks/use-calculate-slippage';
import { Side, OrderType } from '@vegaprotocol/types';
import { DealTicketSlippage } from './deal-ticket-slippage';
interface DealTicketMarketProps {
market: DealTicketQuery_market;
@@ -64,26 +58,28 @@ export const DealTicketSteps = ({
defaultValues: getDefaultOrder(market),
});
const emptyString = ' - ';
const [max, setMax] = useState<number | null>(null);
const step = toDecimal(market.positionDecimalPlaces);
const orderType = watch('type');
const orderTimeInForce = watch('timeInForce');
const orderSide = watch('side');
const orderSize = watch('size');
const order = watch();
const { message: invalidText, isDisabled } = useOrderValidation({
market,
orderType,
orderTimeInForce,
fieldErrors: errors,
});
const { submit, transaction, finalizedOrder, Dialog } = useOrderSubmit();
const estCloseOut = useOrderCloseOut({ order, market, partyData });
const { keypair } = useVegaWallet();
const estMargin = useOrderMargin({
order,
market,
partyId: keypair?.pub || '',
});
const value = new BigNumber(orderSize).toNumber();
const price =
market.depth.lastTrade &&
addDecimal(market.depth.lastTrade.price, market.decimalPlaces);
const emptyString = ' - ';
const [notionalSize, setNotionalSize] = useState<string | null>(null);
const [fees, setFees] = useState<string | null>(null);
const maxTrade = useMaximumPositionSize({
partyId: keypair?.pub || '',
@@ -95,47 +91,45 @@ export const DealTicketSteps = ({
order,
});
const estCloseOut = useOrderCloseOut({ order, market, partyData });
const slippage = useCalculateSlippage({ marketId: market.id, order });
const [slippageValue, setSlippageValue] = useState(
slippage ? parseFloat(slippage) : 0
);
const transactionStatus =
transaction.status === VegaTxStatus.Requested ||
transaction.status === VegaTxStatus.Pending
? 'pending'
: 'default';
useEffect(() => {
setMax(
new BigNumber(maxTrade)
.decimalPlaces(market.positionDecimalPlaces)
.toNumber()
);
}, [maxTrade, market.positionDecimalPlaces]);
const { message: invalidText, isDisabled } = useOrderValidation({
market,
orderType,
orderTimeInForce,
fieldErrors: errors,
});
const { submit, transaction, finalizedOrder, TransactionDialog } =
useOrderSubmit(market);
const onSizeChange = (value: number[]) => {
const newVal = new BigNumber(value[0])
.decimalPlaces(market.positionDecimalPlaces)
.toString();
const isValid = validateSize(step)(newVal);
if (isValid !== 'step') {
setValue('size', newVal);
}
};
useEffect(() => {
setSlippageValue(slippage ? parseFloat(slippage) : 0);
}, [slippage]);
const price = useMemo(() => {
if (slippage && market?.depth?.lastTrade?.price) {
const isLong = order.side === Side.SIDE_BUY;
const multiplier = new BigNumber(1)[isLong ? 'plus' : 'minus'](
parseFloat(slippage) / 100
);
return new BigNumber(market?.depth?.lastTrade?.price)
.multipliedBy(multiplier)
if (market?.depth?.lastTrade?.price) {
const size = new BigNumber(market.depth.lastTrade.price)
.multipliedBy(value)
.toNumber();
setNotionalSize(addDecimal(size, market.decimalPlaces));
}
return null;
}, [market?.depth?.lastTrade?.price, order.side, slippage]);
}, [market, value]);
const formattedPrice =
price && addDecimalsFormatNumber(price, market.decimalPlaces);
const notionalSize = useMemo(() => {
if (price) {
const size = new BigNumber(price).multipliedBy(orderSize).toNumber();
return addDecimalsFormatNumber(size, market.decimalPlaces);
}
return null;
}, [market.decimalPlaces, orderSize, price]);
const fees = useMemo(() => {
useEffect(() => {
if (estMargin?.fees && notionalSize) {
const percentage = new BigNumber(estMargin?.fees)
.dividedBy(notionalSize)
@@ -143,82 +137,23 @@ export const DealTicketSteps = ({
.decimalPlaces(2)
.toString();
return `${estMargin.fees} (${percentage}%)`;
setFees(`${estMargin.fees} (${percentage}%)`);
}
}, [estMargin, notionalSize]);
return null;
}, [estMargin?.fees, notionalSize]);
const transactionStatus =
transaction.status === VegaTxStatus.Requested ||
transaction.status === VegaTxStatus.Pending
? 'pending'
: 'default';
const max = useMemo(() => {
return new BigNumber(maxTrade)
.decimalPlaces(market.positionDecimalPlaces)
.toNumber();
}, [market.positionDecimalPlaces, maxTrade]);
const onSizeChange = useCallback(
(value: number) => {
const newVal = new BigNumber(value)
.decimalPlaces(market.positionDecimalPlaces)
.toString();
const isValid = validateSize(step)(newVal);
if (isValid !== 'step') {
setValue('size', newVal);
}
},
[market.positionDecimalPlaces, setValue, step]
);
const onSlippageChange = useCallback(
(value: number) => {
if (market?.depth?.lastTrade?.price) {
if (value) {
const isLong = order.side === Side.SIDE_BUY;
const multiplier = new BigNumber(1)[isLong ? 'plus' : 'minus'](
value / 100
);
const bestAskPrice = new BigNumber(market?.depth?.lastTrade?.price)
.multipliedBy(multiplier)
.decimalPlaces(market.decimalPlaces)
.toString();
setValue('price', bestAskPrice);
if (orderType === OrderType.TYPE_MARKET) {
setValue('type', OrderType.TYPE_LIMIT);
}
} else {
setValue('type', OrderType.TYPE_MARKET);
setValue('price', market?.depth?.lastTrade?.price);
}
}
setSlippageValue(value);
},
[
market.decimalPlaces,
market?.depth?.lastTrade?.price,
order.side,
orderType,
setValue,
]
);
const onSubmit = useCallback(
const onSubmit = React.useCallback(
(order: Order) => {
if (transactionStatus !== 'pending') {
submit({
...order,
price:
order.price && removeDecimal(order.price, market.decimalPlaces),
size: removeDecimal(order.size, market.positionDecimalPlaces),
});
submit(order);
}
},
[
transactionStatus,
submit,
market.decimalPlaces,
market.positionDecimalPlaces,
]
[transactionStatus, submit]
);
const steps = [
@@ -231,7 +166,7 @@ export const DealTicketSteps = ({
ItemRenderer={MarketNameRenderer}
/>
),
value: market.tradableInstrument.instrument.name,
value: market.name,
},
{
label: t('Select Direction'),
@@ -250,30 +185,24 @@ export const DealTicketSteps = ({
label: t('Choose Position Size'),
component:
max !== null ? (
<>
<DealTicketSize
step={step}
min={step}
max={max}
onSizeChange={onSizeChange}
size={new BigNumber(orderSize).toNumber()}
name="size"
price={formattedPrice || emptyString}
positionDecimalPlaces={market.positionDecimalPlaces}
quoteName={
market.tradableInstrument.instrument.product.settlementAsset
.symbol
}
notionalSize={notionalSize || emptyString}
estCloseOut={estCloseOut}
fees={fees || emptyString}
estMargin={estMargin?.margin || emptyString}
/>
<DealTicketSlippage
value={slippageValue}
onValueChange={onSlippageChange}
/>
</>
<DealTicketSize
step={step}
min={step}
max={max}
onValueChange={onSizeChange}
value={new BigNumber(orderSize).toNumber()}
name="size"
price={price || emptyString}
positionDecimalPlaces={market.positionDecimalPlaces}
quoteName={
market.tradableInstrument.instrument.product.settlementAsset
.symbol
}
notionalSize={notionalSize || emptyString}
estCloseOut={estCloseOut}
fees={fees || emptyString}
estMargin={estMargin?.margin || emptyString}
/>
) : (
'loading...'
),
@@ -284,11 +213,9 @@ export const DealTicketSteps = ({
component: (
<div className="mb-8">
{invalidText && (
<div className="mb-2">
<InputError data-testid="dealticket-error-message">
{invalidText}
</InputError>
</div>
<InputError className="mb-8" data-testid="dealticket-error-message">
{invalidText}
</InputError>
)}
<ReviewTrade
market={market}
@@ -297,22 +224,21 @@ export const DealTicketSteps = ({
order={order}
estCloseOut={estCloseOut}
estMargin={estMargin?.margin || emptyString}
price={formattedPrice || emptyString}
price={price || emptyString}
quoteName={
market.tradableInstrument.instrument.product.settlementAsset
.symbol
}
notionalSize={notionalSize || emptyString}
fees={fees || emptyString}
slippage={slippageValue}
/>
<Dialog
<TransactionDialog
title={getOrderDialogTitle(finalizedOrder?.status)}
intent={getOrderDialogIntent(finalizedOrder?.status)}
icon={getOrderDialogIcon(finalizedOrder?.status)}
>
<OrderFeedback transaction={transaction} order={finalizedOrder} />
</Dialog>
</TransactionDialog>
</div>
),
disabled: true,
@@ -320,7 +246,7 @@ export const DealTicketSteps = ({
];
return (
<form onSubmit={handleSubmit(onSubmit)} className="px-2 py-4">
<form onSubmit={handleSubmit(onSubmit)} className="px-4 py-8">
<Stepper steps={steps} />
</form>
);
@@ -43,7 +43,6 @@ interface Props {
price: string;
fees: string;
notionalSize: string;
slippage: number;
}
export default ({
@@ -56,7 +55,6 @@ export default ({
fees,
price,
notionalSize,
slippage,
}: Props) => {
const { data: tagsData } = useQuery<MarketTags, MarketTagsVariables>(
MARKET_TAGS_QUERY,
@@ -69,7 +67,7 @@ export default ({
<div className="mb-8 text-black dark:text-white" data-testid="review-trade">
<KeyValueTable>
<KeyValueTableRow noBorder>
<div className="flex flex-none gap-x-2 items-center">
<div className="flex flex-none gap-x-5 items-center">
<div
className={classNames(
{
@@ -77,7 +75,7 @@ export default ({
'sellButton dark:sellButtonDark':
order.side === Side.SIDE_SELL,
},
'px-2 py-1 inline text-ui-small'
'px-8 py-4 inline text-ui-small'
)}
>
{SIDE_NAMES[order.side]}
@@ -107,20 +105,19 @@ export default ({
fees={fees}
estCloseOut={estCloseOut}
notionalSize={notionalSize}
slippage={slippage.toString()}
/>
<div className="mt-12 max-w-sm">
<Button
fill={true}
type="submit"
disabled={transactionStatus === 'pending' || isDisabled}
data-testid="place-order"
rightIcon="arrow-top-right"
>
{transactionStatus === 'pending' ? t('Pending...') : t('Submit')}
</Button>
</div>
<Button
className="w-full !py-8 mt-64 max-w-sm"
boxShadow={false}
variant="secondary"
type="submit"
disabled={transactionStatus === 'pending' || isDisabled}
data-testid="place-order"
appendIconName="arrow-top-right"
>
{transactionStatus === 'pending' ? t('Pending...') : t('Submit')}
</Button>
</div>
);
};
@@ -1,6 +1,6 @@
import React from 'react';
import classNames from 'classnames';
import { FormGroup } from '@vegaprotocol/ui-toolkit';
import { FormGroup, Button } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers';
import { Side } from '@vegaprotocol/types';
@@ -19,36 +19,36 @@ export default ({ value, onSelect }: SideSelectorProps) => {
<FormGroup
label={t('Direction')}
labelFor="order-side-toggle"
hideLabel={true}
labelClassName="sr-only"
>
<fieldset
className="w-full grid md:grid-cols-2 gap-4"
className="w-full grid md:grid-cols-2 gap-20"
id="order-side-toggle"
>
<button
<Button
variant="inline-link"
aria-label={t('Open long position')}
type="button"
className={classNames(
'px-8 py-2',
'py-8',
'buyButton hover:buyButton dark:buyButtonDark dark:hover:buyButtonDark',
{ selected: value === Side.SIDE_BUY }
)}
onClick={() => onSelect(Side.SIDE_BUY)}
>
{t('Long')}
</button>
<button
</Button>
<Button
variant="inline-link"
aria-label={t('Open short position')}
type="button"
className={classNames(
'px-8 py-2',
'py-8',
'sellButton hover:sellButton dark:sellButtonDark dark:hover:sellButtonDark',
{ selected: value === Side.SIDE_SELL }
)}
onClick={() => onSelect(Side.SIDE_SELL)}
>
{t('Short')}
</button>
</Button>
<div className="md:col-span-2 text-black dark:text-white text-ui-small">
{t(
'Trading derivatives allows you to make a profit or loss regardless of whether the market you are trading goes up or down. If you open a "long" position, you will make a profit if the price of your chosen market goes up, and you will make a profit for "short" positions when the price goes down.'
@@ -1,17 +0,0 @@
query Deposits {
assetsConnection {
edges {
node {
id
name
symbol
decimals
source {
... on ERC20 {
contractAddress
}
}
}
}
}
}
@@ -1,72 +0,0 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { AssetStatus } from "./../../../../../../../libs/types/src/__generated__/globalTypes";
// ====================================================
// GraphQL query operation: Deposits
// ====================================================
export interface Deposits_assetsConnection_edges_node_source_BuiltinAsset {
__typename: "BuiltinAsset";
}
export interface Deposits_assetsConnection_edges_node_source_ERC20 {
__typename: "ERC20";
/**
* The address of the erc20 contract
*/
contractAddress: string;
}
export type Deposits_assetsConnection_edges_node_source = Deposits_assetsConnection_edges_node_source_BuiltinAsset | Deposits_assetsConnection_edges_node_source_ERC20;
export interface Deposits_assetsConnection_edges_node {
__typename: "Asset";
/**
* The id of the asset
*/
id: string;
/**
* The full name of the asset (e.g: Great British Pound)
*/
name: string;
/**
* The symbol of the asset (e.g: GBP)
*/
symbol: string;
/**
* The precision of the asset
*/
decimals: number;
/**
* The status of the asset in the vega network
*/
status: AssetStatus;
/**
* The origin source of the asset (e.g: an erc20 asset)
*/
source: Deposits_assetsConnection_edges_node_source;
}
export interface Deposits_assetsConnection_edges {
__typename: "AssetEdge";
node: Deposits_assetsConnection_edges_node;
}
export interface Deposits_assetsConnection {
__typename: "AssetsConnection";
/**
* The assets
*/
edges: (Deposits_assetsConnection_edges | null)[] | null;
}
export interface Deposits {
/**
* The list of all assets in use in the vega network or the specified asset if id is provided
*/
assetsConnection: Deposits_assetsConnection;
}
@@ -1,57 +0,0 @@
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type DepositsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type DepositsQuery = { __typename?: 'Query', assetsConnection: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, source: { __typename?: 'BuiltinAsset' } | { __typename?: 'ERC20', contractAddress: string } } } | null> | null } };
export const DepositsDocument = gql`
query Deposits {
assetsConnection {
edges {
node {
id
name
symbol
decimals
source {
... on ERC20 {
contractAddress
}
}
}
}
}
}
`;
/**
* __useDepositsQuery__
*
* To run a query within a React component, call `useDepositsQuery` and pass it any options that fit your needs.
* When your component renders, `useDepositsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useDepositsQuery({
* variables: {
* },
* });
*/
export function useDepositsQuery(baseOptions?: Apollo.QueryHookOptions<DepositsQuery, DepositsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<DepositsQuery, DepositsQueryVariables>(DepositsDocument, options);
}
export function useDepositsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<DepositsQuery, DepositsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<DepositsQuery, DepositsQueryVariables>(DepositsDocument, options);
}
export type DepositsQueryHookResult = ReturnType<typeof useDepositsQuery>;
export type DepositsLazyQueryHookResult = ReturnType<typeof useDepositsLazyQuery>;
export type DepositsQueryResult = Apollo.QueryResult<DepositsQuery, DepositsQueryVariables>;
@@ -1,61 +0,0 @@
import { gql, useQuery } from '@apollo/client';
import { DepositManager } from '@vegaprotocol/deposits';
import { getEnabledAssets, t } from '@vegaprotocol/react-helpers';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { Web3Container } from '@vegaprotocol/web3';
import type { Deposits } from './__generated__/Deposits';
const DEPOSITS_QUERY = gql`
query Deposits {
assetsConnection {
edges {
node {
id
name
symbol
decimals
status
source {
... on ERC20 {
contractAddress
}
}
}
}
}
}
`;
/**
* Fetches data required for the Deposit page
*/
export const DepositContainer = () => {
const { VEGA_ENV } = useEnvironment();
const { keypair } = useVegaWallet();
const { data, loading, error } = useQuery<Deposits>(DEPOSITS_QUERY, {
variables: { partyId: keypair?.pub },
skip: !keypair?.pub,
});
const assets = getEnabledAssets(data);
return (
<AsyncRenderer<Deposits> data={data} loading={loading} error={error}>
{assets.length ? (
<Web3Container>
<DepositManager
assets={assets}
isFaucetable={VEGA_ENV !== Networks.MAINNET}
/>
</Web3Container>
) : (
<Splash>
<p>{t('No assets on this network')}</p>
</Splash>
)}
</AsyncRenderer>
);
};
@@ -1 +0,0 @@
export * from './deposits';
@@ -9,7 +9,7 @@ interface Props {
export const DrawerContent = ({ children, className = '' }: Props) => {
const classes = classNames(
'w-full sm:w-full grow-1 overflow-hidden',
'w-full sm:w-full grow-1 p-12 md:p-20 overflow-hidden',
className
);
@@ -1,6 +1,6 @@
import * as React from 'react';
import classNames from 'classnames';
import { ButtonLink, Icon } from '@vegaprotocol/ui-toolkit';
import { Button, Icon } from '@vegaprotocol/ui-toolkit';
import { IconNames } from '@blueprintjs/icons';
import type { IconName } from '@vegaprotocol/ui-toolkit';
import { useEffect, useState } from 'react';
@@ -37,10 +37,13 @@ export const DrawerToggle = ({
} Sidebar Navigation Menu`;
return (
<span className={classes}>
<ButtonLink aria-label={ariaLabel} onClick={onToggle}>
<Icon name={iconName as IconName} />
</ButtonLink>
</span>
<Button
aria-label={ariaLabel}
variant="inline-link"
className={classes}
onClick={onToggle}
>
<Icon name={iconName as IconName} />
</Button>
);
};
@@ -7,25 +7,23 @@ import LocalContext from '../../context/local-context';
const Header = () => {
const {
vegaWalletDialog: { setConnect, setManage },
theme,
toggleTheme,
} = useContext(LocalContext);
return (
<div
className="flex items-stretch pr-6 py-6 bg-black text-neutral-400"
className="flex items-stretch pr-16 py-16 bg-black text-white-60"
data-testid="header"
>
<Logo />
<div className="flex items-center gap-2 ml-auto relative z-10">
<div className="flex items-center gap-4 ml-auto mr-8 relative z-10">
<VegaWalletConnectButton
setConnectDialog={setConnect}
setManageDialog={setManage}
/>
<ThemeSwitcher
theme={theme}
onToggle={toggleTheme}
className="-my-4"
fixedBg="dark"
sunClassName="text-white"
/>
</div>
</div>
@@ -6,11 +6,11 @@ import { VLogo } from '@vegaprotocol/ui-toolkit';
const Logo = () => {
return (
<NavLink
className="mx-6 text-white"
className="mx-20 text-white"
aria-label={t('Go to home page')}
to="/"
>
<VLogo className="mx-6 my-2" />
<VLogo />
</NavLink>
);
};
@@ -1 +0,0 @@
export * from './input-setter';
@@ -1,64 +0,0 @@
import React from 'react';
import { render, waitFor, fireEvent } from '@testing-library/react';
import { InputSetter } from './index';
describe('InputSetter Component', () => {
it('should show the correct value and visibility based on props', async () => {
const value = 'Hello';
const isInputVisible = true;
const { getByRole } = await render(
<InputSetter
id="input-order-size-market"
type="number"
className="w-full"
value={value}
isInputVisible={isInputVisible}
onChange={() => false}
/>
);
const input = getByRole('spinbutton');
const btn = getByRole('button');
expect(input).toHaveAttribute('value', value);
expect(btn).toBeTruthy();
});
it('should toggle the visibility of the input when the button is clicked', async () => {
const value = 'Hello';
const isInputVisible = true;
const { getByRole } = await render(
<InputSetter
id="input-order-size-market"
type="number"
className="w-full"
value={value}
isInputVisible={isInputVisible}
onChange={() => false}
/>
);
const btn = getByRole('button');
fireEvent.click(btn);
await waitFor(() => getByRole('button'));
expect(getByRole('button')).toHaveTextContent(value);
});
it('should toggle the visibility of the input when the enter key is pressed', async () => {
const value = 'Hello';
const isInputVisible = true;
const { getByRole } = await render(
<InputSetter
id="input-order-size-market"
name="input-order-size-market"
type="number"
className="w-full"
value={value}
isInputVisible={isInputVisible}
onChange={() => false}
/>
);
fireEvent.keyDown(getByRole('spinbutton'), {
key: 'Enter',
});
await waitFor(() => getByRole('button'));
expect(getByRole('button')).toHaveTextContent(value);
});
});
@@ -1,55 +0,0 @@
import React, { useCallback, useState } from 'react';
import type { ReactNode } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import { Input } from '@vegaprotocol/ui-toolkit';
import type { InputProps } from '@vegaprotocol/ui-toolkit';
interface InputSetterProps {
buttonLabel?: string;
isInputVisible?: boolean;
children?: ReactNode;
}
export const InputSetter = ({
buttonLabel = t('set'),
isInputVisible = false,
children,
...props
}: InputSetterProps & InputProps) => {
const [isInputToggled, setIsInputToggled] = useState(isInputVisible);
const toggleInput = useCallback(() => {
setIsInputToggled(!isInputToggled);
}, [isInputToggled]);
const onInputEnter = useCallback(
(event: React.KeyboardEvent) => {
if (event.key === 'Enter') {
event.stopPropagation();
toggleInput();
}
},
[toggleInput]
);
return isInputToggled ? (
<div className="flex items-center">
<Input {...props} onKeyDown={onInputEnter} />
<button
type="button"
className="no-underline hover:underline text-blue ml-2"
onClick={toggleInput}
>
{buttonLabel}
</button>
</div>
) : (
<button
type="button"
className="no-underline hover:underline text-blue py-1.5"
onClick={toggleInput}
>
{children || props.value}
</button>
);
};
@@ -29,7 +29,7 @@ export const Main = () => {
variant={DRAWER_TOGGLE_VARIANTS.CLOSE}
className="p-16"
/>
<Nav className="hidden md:block my-6 h-full" />
<Nav className="hidden md:block my-20 h-full" />
</NavigationDrawer>
<DrawerContent>
<AppRouter />
@@ -9,7 +9,7 @@ interface NavItemProps {
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-2" />
<Icon name={iconName} className="mr-8" />
<span className="text-lg">{label}</span>
</div>
);
@@ -19,7 +19,7 @@ export const Nav = ({ className, tabs = false }: NavProps) => {
key={r.name}
to={r.path}
className={({ isActive }) =>
`text-base block md:mb-10 px-12 md:text-black md:dark:text-white ${
`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'
}`
}
@@ -8,7 +8,7 @@ interface TabBarProps {
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-4 border-t border-neutral-300 dark:border-neutral-700">
<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>
@@ -7,16 +7,11 @@ import { OrderListContainer } from '@vegaprotocol/orders';
import { PositionsContainer } from '@vegaprotocol/positions';
import { FillsContainer } from '@vegaprotocol/fills';
import ConnectWallet from '../wallet-connector';
import { DepositContainer } from '../deposits';
export const Portfolio = () => {
const { keypair } = useVegaWallet();
if (!keypair) {
return (
<section className="xl:w-1/2">
<ConnectWallet />
</section>
);
return <ConnectWallet />;
}
return (
<Tabs>
@@ -32,9 +27,6 @@ export const Portfolio = () => {
<Tab id="fills" name={t('Fills')}>
<FillsContainer />
</Tab>
<Tab id="deposits" name={t('Deposits')}>
<DepositContainer />
</Tab>
</Tabs>
);
};
@@ -1,5 +0,0 @@
subscription CandleLive($marketId: ID!) {
candles(marketId: $marketId, interval: INTERVAL_I1H) {
close
}
}
@@ -1,44 +0,0 @@
fragment SimpleMarketDataFields on MarketData {
market {
id
state
}
}
query SimpleMarkets($CandleSince: String!) {
markets {
id
name
state
data {
...SimpleMarketDataFields
}
tradableInstrument {
instrument {
code
metadata {
tags
}
product {
__typename
... on Future {
quoteName
settlementAsset {
symbol
}
}
}
}
}
candles(interval: INTERVAL_I1H, since: $CandleSince) {
open
close
}
}
}
subscription SimpleMarketDataSub {
marketData {
...SimpleMarketDataFields
}
}
@@ -3,7 +3,7 @@
// @generated
// This file was automatically generated and should not be edited.
import { MarketState } from "./../../../../../../../libs/types/src/__generated__/globalTypes";
import { MarketState } from "@vegaprotocol/types";
// ====================================================
// GraphQL subscription operation: SimpleMarketDataSub
@@ -24,7 +24,7 @@ export interface SimpleMarketDataSub_marketData_market {
export interface SimpleMarketDataSub_marketData {
__typename: "MarketData";
/**
* market id of the associated mark price
* market ID of the associated mark price
*/
market: SimpleMarketDataSub_marketData_market;
}
@@ -3,12 +3,32 @@
// @generated
// This file was automatically generated and should not be edited.
import { MarketState } from "./../../../../../../../libs/types/src/__generated__/globalTypes";
import { MarketState } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: SimpleMarkets
// ====================================================
export interface SimpleMarkets_markets_data_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
/**
* Current state of the market
*/
state: MarketState;
}
export interface SimpleMarkets_markets_data {
__typename: "MarketData";
/**
* market ID of the associated mark price
*/
market: SimpleMarkets_markets_data_market;
}
export interface SimpleMarkets_markets_tradableInstrument_instrument_metadata {
__typename: "InstrumentMetadata";
/**
@@ -43,10 +63,6 @@ export interface SimpleMarkets_markets_tradableInstrument_instrument {
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
*/
code: string;
/**
* Full and fairly descriptive name for the instrument
*/
name: string;
/**
* Metadata for this instrument
*/
@@ -60,7 +76,7 @@ export interface SimpleMarkets_markets_tradableInstrument_instrument {
export interface SimpleMarkets_markets_tradableInstrument {
__typename: "TradableInstrument";
/**
* An instance of or reference to a fully specified instrument.
* An instance of, or reference to, a fully specified instrument.
*/
instrument: SimpleMarkets_markets_tradableInstrument_instrument;
}
@@ -83,16 +99,24 @@ export interface SimpleMarkets_markets {
* Market ID
*/
id: string;
/**
* Market full name
*/
name: string;
/**
* Current state of the market
*/
state: MarketState;
/**
* An instance of or reference to a tradable instrument.
* marketData for the given market
*/
data: SimpleMarkets_markets_data | null;
/**
* An instance of, or reference to, a tradable instrument.
*/
tradableInstrument: SimpleMarkets_markets_tradableInstrument;
/**
* Candles on a market, for the 'last' n candles, at 'interval' seconds as specified by params
* Candles on a market, for the 'last' n candles, at 'interval' seconds as specified by parameters
*/
candles: (SimpleMarkets_markets_candles | null)[] | null;
}
@@ -1,43 +0,0 @@
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type CandleLiveSubscriptionVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type CandleLiveSubscription = { __typename?: 'Subscription', candles: { __typename?: 'Candle', close: string } };
export const CandleLiveDocument = gql`
subscription CandleLive($marketId: ID!) {
candles(marketId: $marketId, interval: INTERVAL_I1H) {
close
}
}
`;
/**
* __useCandleLiveSubscription__
*
* To run a query within a React component, call `useCandleLiveSubscription` and pass it any options that fit your needs.
* When your component renders, `useCandleLiveSubscription` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useCandleLiveSubscription({
* variables: {
* marketId: // value for 'marketId'
* },
* });
*/
export function useCandleLiveSubscription(baseOptions: Apollo.SubscriptionHookOptions<CandleLiveSubscription, CandleLiveSubscriptionVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSubscription<CandleLiveSubscription, CandleLiveSubscriptionVariables>(CandleLiveDocument, options);
}
export type CandleLiveSubscriptionHookResult = ReturnType<typeof useCandleLiveSubscription>;
export type CandleLiveSubscriptionResult = Apollo.SubscriptionResult<CandleLiveSubscription>;
@@ -1,117 +0,0 @@
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type SimpleMarketDataFieldsFragment = { __typename?: 'MarketData', market: { __typename?: 'Market', id: string, state: Types.MarketState } };
export type SimpleMarketsQueryVariables = Types.Exact<{
CandleSince: Types.Scalars['String'];
}>;
export type SimpleMarketsQuery = { __typename?: 'Query', markets?: Array<{ __typename?: 'Market', id: string, name: string, state: Types.MarketState, data?: { __typename?: 'MarketData', market: { __typename?: 'Market', id: string, state: Types.MarketState } } | null, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', symbol: string } } } }, candles?: Array<{ __typename?: 'Candle', open: string, close: string } | null> | null }> | null };
export type SimpleMarketDataSubSubscriptionVariables = Types.Exact<{ [key: string]: never; }>;
export type SimpleMarketDataSubSubscription = { __typename?: 'Subscription', marketData: { __typename?: 'MarketData', market: { __typename?: 'Market', id: string, state: Types.MarketState } } };
export const SimpleMarketDataFieldsFragmentDoc = gql`
fragment SimpleMarketDataFields on MarketData {
market {
id
state
}
}
`;
export const SimpleMarketsDocument = gql`
query SimpleMarkets($CandleSince: String!) {
markets {
id
name
state
data {
...SimpleMarketDataFields
}
tradableInstrument {
instrument {
code
metadata {
tags
}
product {
__typename
... on Future {
quoteName
settlementAsset {
symbol
}
}
}
}
}
candles(interval: INTERVAL_I1H, since: $CandleSince) {
open
close
}
}
}
${SimpleMarketDataFieldsFragmentDoc}`;
/**
* __useSimpleMarketsQuery__
*
* To run a query within a React component, call `useSimpleMarketsQuery` and pass it any options that fit your needs.
* When your component renders, `useSimpleMarketsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useSimpleMarketsQuery({
* variables: {
* CandleSince: // value for 'CandleSince'
* },
* });
*/
export function useSimpleMarketsQuery(baseOptions: Apollo.QueryHookOptions<SimpleMarketsQuery, SimpleMarketsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<SimpleMarketsQuery, SimpleMarketsQueryVariables>(SimpleMarketsDocument, options);
}
export function useSimpleMarketsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<SimpleMarketsQuery, SimpleMarketsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<SimpleMarketsQuery, SimpleMarketsQueryVariables>(SimpleMarketsDocument, options);
}
export type SimpleMarketsQueryHookResult = ReturnType<typeof useSimpleMarketsQuery>;
export type SimpleMarketsLazyQueryHookResult = ReturnType<typeof useSimpleMarketsLazyQuery>;
export type SimpleMarketsQueryResult = Apollo.QueryResult<SimpleMarketsQuery, SimpleMarketsQueryVariables>;
export const SimpleMarketDataSubDocument = gql`
subscription SimpleMarketDataSub {
marketData {
...SimpleMarketDataFields
}
}
${SimpleMarketDataFieldsFragmentDoc}`;
/**
* __useSimpleMarketDataSubSubscription__
*
* To run a query within a React component, call `useSimpleMarketDataSubSubscription` and pass it any options that fit your needs.
* When your component renders, `useSimpleMarketDataSubSubscription` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useSimpleMarketDataSubSubscription({
* variables: {
* },
* });
*/
export function useSimpleMarketDataSubSubscription(baseOptions?: Apollo.SubscriptionHookOptions<SimpleMarketDataSubSubscription, SimpleMarketDataSubSubscriptionVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSubscription<SimpleMarketDataSubSubscription, SimpleMarketDataSubSubscriptionVariables>(SimpleMarketDataSubDocument, options);
}
export type SimpleMarketDataSubSubscriptionHookResult = ReturnType<typeof useSimpleMarketDataSubSubscription>;
export type SimpleMarketDataSubSubscriptionResult = Apollo.SubscriptionResult<SimpleMarketDataSubSubscription>;
@@ -3,7 +3,6 @@ import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
import { MarketState } from '@vegaprotocol/types';
import { IS_MARKET_TRADABLE } from '../../constants';
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
import colors from 'tailwindcss/colors';
export const STATES_FILTER = [
{ value: 'all', text: t('All') },
@@ -37,7 +36,7 @@ export const agGridLightVariables = `
overflow: visible;
}
.ag-theme-balham .ag-row-hover:not(.mobile) {
--ag-row-border-color: ${colors.black};
--ag-row-border-color: ${theme.colors.black[100]};
}
.ag-theme-balham .ag-row-hover .icon-green-hover {
fill: ${theme.colors.darkerGreen};
@@ -124,7 +123,7 @@ export const agGridDarkVariables = `
overflow: visible;
}
.ag-theme-balham-dark .ag-row-hover:not(.mobile){
--ag-row-border-color: ${colors.white};
--ag-row-border-color: ${theme.colors.white[100]};
}
.ag-theme-balham-dark .ag-row-hover .icon-green-hover {
fill: ${theme.colors.lightGreen};
@@ -195,5 +194,3 @@ export const ROW_CLASS_RULES = {
'cursor-pointer': ({ data }: { data: SimpleMarkets_markets }) =>
IS_MARKET_TRADABLE(data || {}),
};
export const LARGE_SCREENS = ['xl', 'xxl'];
@@ -10,15 +10,28 @@ import type {
SimpleMarketDataSub_marketData,
} from './__generated__/SimpleMarketDataSub';
const MARKET_DATA_FRAGMENT = gql`
fragment SimpleMarketDataFields on MarketData {
market {
id
state
}
}
`;
export const MARKETS_QUERY = gql`
${MARKET_DATA_FRAGMENT}
query SimpleMarkets($CandleSince: String!) {
markets {
id
name
state
data {
...SimpleMarketDataFields
}
tradableInstrument {
instrument {
code
name
metadata {
tags
}
@@ -33,7 +46,7 @@ export const MARKETS_QUERY = gql`
}
}
}
candles(interval: I1H, since: $CandleSince) {
candles(interval: INTERVAL_I1H, since: $CandleSince) {
open
close
}
@@ -42,19 +55,17 @@ export const MARKETS_QUERY = gql`
`;
const MARKET_DATA_SUB = gql`
${MARKET_DATA_FRAGMENT}
subscription SimpleMarketDataSub {
marketData {
market {
id
state
}
...SimpleMarketDataFields
}
}
`;
export const CANDLE_SUB = gql`
subscription CandleLive($marketId: ID!) {
candles(marketId: $marketId, interval: I1H) {
candles(marketId: $marketId, interval: INTERVAL_I1H) {
close
}
}
@@ -67,7 +78,7 @@ const update = (
return produce(data, (draft) => {
const index = draft.findIndex((m) => m.id === delta.market.id);
if (index !== -1) {
draft[index].state = delta.market.state;
draft[index].data = delta;
}
// @TODO - else push new market to draft
});
@@ -83,12 +94,6 @@ export const dataProvider = makeDataProvider<
SimpleMarkets_markets[],
SimpleMarketDataSub,
SimpleMarketDataSub_marketData
>({
query: MARKETS_QUERY,
subscriptionQuery: MARKET_DATA_SUB,
update,
getData,
getDelta,
});
>(MARKETS_QUERY, MARKET_DATA_SUB, update, getData, getDelta);
export default dataProvider;
@@ -22,7 +22,7 @@ const SimpleMarketExpires = ({
return agg;
}, null);
return dateFound ? (
<div className="p-2 text-ui-small border border-pink text-pink inline-block">{`${format(
<div className="p-2 text-ui-small border-1 border-pink text-pink inline-block">{`${format(
dateFound as Date,
EXPIRE_DATE_FORMAT
)}`}</div>
@@ -6,19 +6,17 @@ import {
waitFor,
cleanup,
getAllByRole,
fireEvent,
} from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing';
import { BrowserRouter } from 'react-router-dom';
import { MarketState } from '@vegaprotocol/types';
import SimpleMarketList from './simple-market-list';
import { MARKETS_QUERY } from './data-provider';
import type {
SimpleMarkets_markets,
SimpleMarkets,
} from './__generated__/SimpleMarkets';
import type { SimpleMarketDataSub_marketData } from './__generated__/SimpleMarketDataSub';
import { MarketState } from '@vegaprotocol/types';
const mockedNavigate = jest.fn();
@@ -32,85 +30,93 @@ jest.mock('date-fns', () => ({
subDays: () => new Date('2022-06-02T11:11:21.721Z'),
}));
let updateMock: ({
delta,
}: {
delta: SimpleMarketDataSub_marketData;
}) => boolean;
let mockData = [
{
id: '1',
name: 'Market 1',
state: MarketState.STATE_ACTIVE,
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
symbol: 'tUSD',
},
},
metadata: {
tags: [],
},
},
},
},
{
id: '2',
name: 'Market 2',
state: MarketState.STATE_ACTIVE,
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
symbol: 'ETH',
},
},
metadata: {
tags: [],
},
},
},
},
] as unknown as SimpleMarkets_markets[];
const mockUseDataProvider = ({ update }: { update: () => boolean }) => {
updateMock = update;
return { data: mockData, loading: false, error: false };
};
jest.mock('@vegaprotocol/react-helpers', () => ({
...jest.requireActual('@vegaprotocol/react-helpers'),
useDataProvider: jest.fn((args) => mockUseDataProvider(args)),
}));
const mockIsTradable = jest.fn((_arg) => true);
jest.mock('../../constants', () => ({
...jest.requireActual('../../constants'),
IS_MARKET_TRADABLE: jest.fn((arg) => mockIsTradable(arg)),
}));
describe('SimpleMarketList', () => {
afterEach(() => {
jest.clearAllMocks();
cleanup();
});
const mocks: MockedResponse<SimpleMarkets> = {
request: {
query: MARKETS_QUERY,
variables: {
CandleSince: '2022-06-02T11:11:21.721Z',
it('should be properly renderer as empty', async () => {
const mocks: MockedResponse<SimpleMarkets> = {
request: {
query: MARKETS_QUERY,
variables: {
CandleSince: '2022-06-02T11:11:21.721Z',
},
},
},
result: {
data: { markets: mockData },
},
};
result: {
data: { markets: [] },
},
};
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
<SimpleMarketList />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(screen.getByText('No data to display')).toBeInTheDocument();
});
});
it('should be properly rendered with some data', async () => {
const data = [
{
id: '1',
data: {
market: {
state: MarketState.STATE_ACTIVE,
},
},
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
symbol: 'tUSD',
},
},
metadata: {
tags: [],
},
},
},
},
{
id: '2',
data: {
market: {
state: MarketState.STATE_ACTIVE,
},
},
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
symbol: 'ETH',
},
},
metadata: {
tags: [],
},
},
},
},
] as unknown as SimpleMarkets_markets[];
const mocks: MockedResponse<SimpleMarkets> = {
request: {
query: MARKETS_QUERY,
variables: {
CandleSince: '2022-06-02T11:11:21.721Z',
},
},
result: {
data: { markets: data },
},
};
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
@@ -130,92 +136,4 @@ describe('SimpleMarketList', () => {
expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(2);
});
});
it('update should return proper boolean value', async () => {
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
<SimpleMarketList />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(
document.querySelector('.ag-center-cols-container')
).toBeInTheDocument();
});
await expect(
updateMock({
delta: {
__typename: 'MarketData',
market: {
__typename: 'Market',
id: '2',
state: MarketState.STATE_ACTIVE,
},
},
})
).toEqual(true);
await expect(
updateMock({
delta: {
__typename: 'MarketData',
market: {
__typename: 'Market',
id: '2',
state: MarketState.STATE_SUSPENDED,
},
},
})
).toEqual(false);
});
it('click on row should be properly handled', async () => {
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
<SimpleMarketList />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(
document.querySelector('.ag-center-cols-container')
).toBeInTheDocument();
});
mockIsTradable.mockClear();
const container = document.querySelector('.ag-center-cols-container');
const firstRow = getAllByRole(container as HTMLDivElement, 'row')[0];
expect(firstRow).toHaveAttribute('row-id', '1');
fireEvent.click(firstRow);
await waitFor(() => {
expect(mockIsTradable).toHaveBeenCalledWith({
...mockData[0],
percentChange: '-',
});
expect(mockedNavigate).toHaveBeenCalledWith(`/trading/${mockData[0].id}`);
});
});
it('should be properly renderer as empty', async () => {
mockData = [];
await act(async () => {
render(
<MockedProvider mocks={[mocks]}>
<SimpleMarketList />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(screen.getByText('No data to display')).toBeInTheDocument();
});
});
});
@@ -45,7 +45,7 @@ export type RouterParams = Partial<{
}>;
const SimpleMarketList = () => {
const { isMobile, screenSize } = useScreenDimensions();
const { isMobile } = useScreenDimensions();
const navigate = useNavigate();
const params = useParams<RouterParams>();
const theme = useContext(ThemeContext);
@@ -80,7 +80,7 @@ const SimpleMarketList = () => {
useEffect(() => {
const statuses: Record<string, MarketState | ''> = {};
data?.forEach((market) => {
statuses[market.id] = market.state || '';
statuses[market.id] = market.data?.market.state || '';
});
statusesRef.current = statuses;
}, [data, statusesRef]);
@@ -129,12 +129,8 @@ const SimpleMarketList = () => {
[handleRowClicked]
);
const shouldSuppressHorizontalScroll = useMemo(() => {
return !isMobile && constants.LARGE_SCREENS.includes(screenSize);
}, [isMobile, screenSize]);
return (
<div className="h-full p-4 md:p-6 grid grid-rows-[min-content,1fr]">
<div className="h-full grid grid-rows-[min-content,1fr]">
<SimpleMarketToolbar data={data || []} />
<AsyncRenderer loading={loading} error={error} data={localData}>
<AgGrid
@@ -160,7 +156,6 @@ const SimpleMarketList = () => {
suppressRowTransform
onCellKeyDown={onCellKeyDown}
tabToNextCell={onTabToNextCell}
suppressHorizontalScroll={shouldSuppressHorizontalScroll}
/>
</AsyncRenderer>
</div>
@@ -1,36 +0,0 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import type { MarketNames_markets } from '@vegaprotocol/deal-ticket';
import { MarketState } from '@vegaprotocol/types';
import MarketNameRenderer from './simple-market-renderer';
describe('SimpleMarketRenderer', () => {
const market = {
id: 'marketId',
state: MarketState.STATE_ACTIVE,
tradableInstrument: {
instrument: {
code: 'Market code',
name: 'Market Name',
product: {
quoteName: 'Quote name',
},
metadata: {
tags: null,
},
},
},
} as MarketNames_markets;
it('should properly render not mobile', () => {
render(<MarketNameRenderer market={market} isMobile={false} />);
expect(screen.getByText('Market Name')).toBeInTheDocument();
expect(screen.getByText('Quote name')).toBeInTheDocument();
});
it('should properly render mobile', () => {
render(<MarketNameRenderer market={market} isMobile={true} />);
expect(screen.getByText('Market code')).toBeInTheDocument();
expect(screen.getByText('Quote name')).toBeInTheDocument();
});
});
@@ -10,16 +10,14 @@ interface Props {
const MarketNameRenderer = ({ market, isMobile }: Props) => {
return (
<div className="flex h-full items-center grid grid-rows-2 grid-flow-col gap-x-2 md:gap-x-4 gap-y-0 grid-cols-[min-content,1fr,1fr] md:pl-4">
<div className="flex h-full items-center grid grid-rows-2 grid-flow-col gap-x-4 md:gap-x-8 gap-y-0 grid-cols-[min-content,1fr,1fr] md:pl-8">
<div
className={classNames(
'row-span-2 bg-pink rounded-full bg-gradient-to-br from-white/40 to-white/80 opacity-30 w-6 h-6 md:w-10 md:h-10'
'row-span-2 bg-pink rounded-full bg-gradient-to-br from-white-60 to--white-80 opacity-30 w-20 h-20 md:w-44 md:h-44'
)}
/>
<div className="col-span-2 uppercase justify-start text-black dark:text-white text-ui-small md:text-market self-end">
{isMobile
? market.tradableInstrument.instrument.code
: market.tradableInstrument.instrument.name}{' '}
{isMobile ? market.tradableInstrument.instrument.code : market.name}{' '}
<SimpleMarketExpires
tags={market.tradableInstrument.instrument.metadata.tags}
/>
@@ -1,35 +1,11 @@
import React from 'react';
import {
useLocation,
useRoutes,
BrowserRouter,
useParams,
} from 'react-router-dom';
import {
render,
screen,
fireEvent,
waitFor,
act,
getAllByText,
} from '@testing-library/react';
import { useLocation, useRoutes, BrowserRouter } from 'react-router-dom';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { MarketState } from '@vegaprotocol/types';
import SimpleMarketToolbar from './simple-market-toolbar';
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
import { markets as filterData } from './mocks/market-filters.json';
const mockedNavigate = jest.fn();
jest.mock('react-router-dom', () => {
const actualRouter = jest.requireActual('react-router-dom');
return {
...actualRouter,
useNavigate: () => mockedNavigate,
useParams: jest.fn(() => actualRouter.useParams()),
};
});
describe('SimpleMarketToolbar', () => {
const WrappedCompForTest = () => {
const routes = useRoutes([
@@ -84,8 +60,9 @@ describe('SimpleMarketToolbar', () => {
</>
);
};
afterEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});
it('should be properly rendered', async () => {
@@ -106,7 +83,7 @@ describe('SimpleMarketToolbar', () => {
expect(screen.getByTestId('market-assets-menu').children).toHaveLength(6);
});
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
expect(screen.getByRole('menu').children).toHaveLength(10);
});
@@ -127,7 +104,7 @@ describe('SimpleMarketToolbar', () => {
await waitFor(() => {
expect(screen.getByTestId('location-display')).toHaveTextContent(
`/markets/${MarketState.STATE_ACTIVE}/Future`
'/markets/STATE_ACTIVE/Future'
);
});
@@ -138,91 +115,17 @@ describe('SimpleMarketToolbar', () => {
);
await waitFor(() => {
expect(screen.getByTestId('location-display')).toHaveTextContent(
`/markets/${MarketState.STATE_ACTIVE}/Future/tEURO`
'/markets/STATE_ACTIVE/Future/tEURO'
);
});
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
const menu = screen.getByRole('menu');
const pending = getAllByText(menu, 'Pending')[0];
await act(() => {
fireEvent.click(pending);
});
await waitFor(() => {
expect(mockedNavigate).toHaveBeenCalledWith(
`/markets/${MarketState.STATE_PENDING}/Future/tEURO`
fireEvent.click(screen.getByText('Pending'));
expect(screen.getByTestId('location-display')).toHaveTextContent(
'/markets/Pending/Future/tEURO'
);
});
});
it('stateChange callback should work well', async () => {
(useParams as jest.Mock).mockImplementation(() => ({
asset: 'asset1',
product: 'product1',
state: 'state1',
}));
render(
<MockedProvider mocks={[]} addTypename={false}>
<SimpleMarketToolbar data={filterData as SimpleMarkets_markets[]} />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
const menu = screen.getByRole('menu');
const suspended = getAllByText(menu, 'Suspended')[0];
fireEvent.click(suspended);
expect(mockedNavigate).toHaveBeenCalledWith(
`/markets/${MarketState.STATE_SUSPENDED}/product1/asset1`
);
});
it('stateChange callback should call navigate with url with state only', async () => {
(useParams as jest.Mock).mockImplementation(() => ({}));
render(
<MockedProvider mocks={[]} addTypename={false}>
<SimpleMarketToolbar data={filterData as SimpleMarkets_markets[]} />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
const menu = screen.getByRole('menu');
const closed = getAllByText(menu, 'Closed')[0];
fireEvent.click(closed);
expect(mockedNavigate).toHaveBeenCalledWith(
`/markets/${MarketState.STATE_CLOSED}`
);
});
it('stateChange callback should call navigate with no asset', async () => {
(useParams as jest.Mock).mockImplementation(() => ({
asset: 'all',
state: 'state1',
}));
render(
<MockedProvider mocks={[]} addTypename={false}>
<SimpleMarketToolbar data={filterData as SimpleMarkets_markets[]} />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
fireEvent.click(screen.getByTestId('state-trigger'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
const menu = screen.getByRole('menu');
const active = getAllByText(menu, 'Active')[0];
fireEvent.click(active);
expect(mockedNavigate).toHaveBeenCalledWith('/markets');
});
});
@@ -75,14 +75,14 @@ const SimpleMarketToolbar = ({ data }: Props) => {
);
return (
<div className="w-full max-w-full mb-2 md:mb-8 font-alpha">
<div className="w-full max-w-full mb-32 font-alpha">
<ul
ref={slideContRef}
className="grid grid-flow-col auto-cols-min gap-4 relative pb-2 mb-2"
className="grid grid-flow-col auto-cols-min gap-8 relative pb-4 mb-16"
data-testid="market-products-menu"
aria-label={t('Product type')}
>
<li key="all-markets" className="md:mr-2 whitespace-nowrap">
<li key="all-markets" className="md:mr-16 whitespace-nowrap">
<Link
to={`/markets${
params.state && params.state !== MarketState.STATE_ACTIVE
@@ -90,7 +90,7 @@ const SimpleMarketToolbar = ({ data }: Props) => {
: ''
}`}
aria-label={t('All markets')}
className={classNames('pl-0 text-pink hover:opacity-75', {
className={classNames('text-h5 pl-0 text-pink hover:opacity-75', {
active: !activeNumber,
})}
>
@@ -98,13 +98,13 @@ const SimpleMarketToolbar = ({ data }: Props) => {
</Link>
</li>
{products.map((product, i) => (
<li key={product} className="mx-2 whitespace-nowrap">
<li key={product} className="mx-16 whitespace-nowrap">
<Link
to={`/markets/${
params.state || MarketState.STATE_ACTIVE
}/${product}`}
className={classNames(
'hover:opacity-75 text-black dark:text-white',
'text-h5 hover:opacity-75 text-black dark:text-white',
{
active: activeNumber - 1 === i,
}
@@ -116,18 +116,17 @@ const SimpleMarketToolbar = ({ data }: Props) => {
</li>
))}
<li
className="absolute bottom-0 h-[2px] transition-left duration-300 dark:bg-white bg-black"
className="absolute bottom-0 h-2 transition-left duration-300 dark:bg-white bg-black"
key="slider"
style={sliderStyles}
/>
</ul>
<div className="grid gap-4 pb-2 mt-2 md:mt-6 md:grid-cols-[min-content,min-content,1fr]">
<div className="pb-2">
<DropdownMenu open={isOpen} onOpenChange={(open) => setOpen(open)}>
<div className="grid gap-8 pb-4 mt-8 md:grid-cols-[min-content,min-content,1fr]">
<div className="pb-8">
<DropdownMenu onOpenChange={(open) => setOpen(open)}>
<DropdownMenuTrigger
className="mr-2 w-auto text-capMenu text-black dark:text-white"
className="mr-16 w-auto text-capMenu text-black dark:text-white"
data-testid="state-trigger"
onClick={() => setOpen(!isOpen)}
>
<div className="w-full justify-between uppercase inline-flex items-center justify-center box-border">
{STATES_FILTER.find(
@@ -138,29 +137,31 @@ const SimpleMarketToolbar = ({ data }: Props) => {
<Icon
name={IconNames.ARROW_DOWN}
className={classNames(
'fill-current ml-2 transition-transform',
'fill-current ml-8 transition-transform',
{
'rotate-180': isOpen,
}
)}
size={16}
/>
</div>
</DropdownMenuTrigger>
<DropdownMenuContent>
{STATES_FILTER.map(({ value, text }) => (
<DropdownMenuCheckboxItem
className="uppercase text-ui"
className="uppercase text-ui dark:text-white"
key={value}
inset
checked={
value === params.state ||
(!params.state && value === MarketState.STATE_ACTIVE)
}
onCheckedChange={() => onStateChange(value)}
>
{text}
<DropdownMenuItemIndicator>
<Icon name="tick" />
</DropdownMenuItemIndicator>
{text}
</DropdownMenuCheckboxItem>
))}
</DropdownMenuContent>
@@ -171,14 +172,14 @@ const SimpleMarketToolbar = ({ data }: Props) => {
</div>
{activeNumber > 0 && (
<ul
className="md:gap-x-6 gap-x-4 gap-y-1 pb-2 md:ml-2 flex flex-wrap"
className="md:gap-16 gap-12 pb-4 md:ml-16 flex flex-wrap"
data-testid="market-assets-menu"
aria-label={t('Asset on the market')}
>
<li key="all">
<Link
to={`/markets/${params.state}/${params.product}`}
className={classNames('uppercase pl-0 md:pl-2 text-capMenu', {
className={classNames('uppercase pl-0 md:pl-4 text-capMenu', {
'text-deemphasise dark:text-midGrey':
params.asset && params.asset !== 'all',
'active text-black dark:text-white':
@@ -25,13 +25,13 @@ export const Counter = ({ label, isActive, className }: CounterProps) => {
const unselected: TStyleMap = {
border: 'border',
size: 'w-[30px] md:w-10',
size: 'w-[30px] md:w-40',
margin: 'mt-[5px] md:ml-[10px] md:mt-0',
};
const selected: TStyleMap = {
colours: 'text-xl text-white',
size: 'w-10 md:w-[60px]',
size: 'w-40 md:w-[60px]',
};
const classes = classNames(
@@ -58,13 +58,13 @@ export const Stepper = ({ steps }: StepperProps) => {
key={`${index}-${step.label}`}
aria-label={t(`Step ${index + 1}`)}
>
<div className="flex relative md:pt-6">
<div className="flex relative md:pt-16">
{!isFirstStep ? (
<div
aria-hidden
className="flex-auto absolute top-[20px] -left-1/2 right-1/2 md:-top-1/2 md:bottom-1/2 md:left-[29.5px] md:right-auto"
>
<span className="h-full block border-t border-black dark:border-white w-full md:border-l md:border-t-0" />
<span className="h-full block border-t-1 border-black dark:border-white w-full md:border-l-1 md:border-t-0" />
</div>
) : undefined}
<button
@@ -79,7 +79,7 @@ export const Stepper = ({ steps }: StepperProps) => {
>
<div className="flex-1 flex flex-col md:flex-row items-center w-full text-center">
<Counter
className="md:mr-4"
className="md:mr-16"
isActive={isActive}
label={(index + 1).toString()}
/>
@@ -87,8 +87,8 @@ export const Stepper = ({ steps }: StepperProps) => {
className={classNames(
'md:mt-0 font-alpha uppercase text-black dark:text-white',
{
'mt-2 text-md md:text-2xl': isActive,
'mt-4 text-sm md:text-lg md:ml-2': !isActive,
'mt-8 text-md md:text-2xl': isActive,
'mt-16 text-sm md:text-lg ml-8': !isActive,
}
)}
>
@@ -134,22 +134,22 @@ export const Stepper = ({ steps }: StepperProps) => {
id={`step-${activeStep}-panel`}
aria-labelledby={`step-${activeStep}-control`}
role="tabpanel"
className="md:hidden mt-10" // md:hidden as fallback
className="md:hidden mt-32" // md:hidden as fallback
/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
tabIndex={0}
>
{steps[activeStep].component}
{!isLastStep && (
<div className="mt-10 md:sr-only">
<Button
fill={true}
onClick={handleNext}
disabled={steps[activeStep].disabled}
data-testid="next-button"
>
{t('Next')}
</Button>
</div>
<Button
className="w-full !py-8 mt-64 md:sr-only"
boxShadow={false}
variant="secondary"
onClick={handleNext}
disabled={steps[activeStep].disabled}
data-testid="next-button"
>
{t('Next')}
</Button>
)}
</div>
)}
@@ -1 +0,0 @@
export * from './traffic-light';
@@ -1,25 +0,0 @@
import React from 'react';
import type { ReactNode } from 'react';
import classNames from 'classnames';
interface TrafficLightProps {
value: number;
q1: number;
q2: number;
children: ReactNode;
}
export const TrafficLight = ({
value,
q1,
q2,
children,
}: TrafficLightProps) => {
const slippageClassName = classNames({
'text-darkerGreen dark:text-lightGreen': value < q1,
'text-amber': value >= q1 && value < q2,
'text-vega-red': value >= q2,
});
return <div className={slippageClassName}>{children || value}</div>;
};
@@ -10,17 +10,19 @@ const ConnectWallet = () => {
} = useContext(LocalContext);
return (
<section
className="p-8 bg-white-normal dark:bg-offBlack"
className="p-32 bg-white-normal dark:bg-offBlack"
data-testid="trading-connect-wallet"
>
<h3 className="mb-4 text-2xl text-offBlack dark:text-white">
<h3 className="mb-16 text-2xl text-offBlack dark:text-white">
{t('Please connect your Vega wallet to make a trade')}
</h3>
<div className="mb-4">
<Button variant="primary" onClick={() => setConnect(true)} size="lg">
{t('Connect Vega wallet')}
</Button>
</div>
<Button
variant="primary"
onClick={() => setConnect(true)}
className="h-[50px] mb-16"
>
{t('Connect Vega wallet')}
</Button>
<h4 className="text-lg text-offBlack dark:text-white">
{t("Don't have a wallet?")}
</h4>
+1 -1
View File
@@ -10,7 +10,7 @@ export const TRADABLE_STATES = {
};
export const IS_MARKET_TRADABLE = (market: SimpleMarkets_markets) =>
Boolean((market.state ?? '') in TRADABLE_STATES && market?.id);
Boolean((market.data?.market.state ?? '') in TRADABLE_STATES && market?.id);
export const MARKET_STATES_MAP: Record<MarketState | '', string> = {
[MarketState.STATE_ACTIVE]: t('Active'),
@@ -16,7 +16,6 @@ interface MenuState {
export interface LocalValues {
vegaWalletDialog: VegaWalletDialogState;
menu: MenuState;
theme: 'light' | 'dark';
toggleTheme: () => void;
}
@@ -1,30 +0,0 @@
query EstimateOrder(
$marketId: ID!
$partyId: ID!
$price: String
$size: String!
$side: Side!
$timeInForce: OrderTimeInForce!
$expiration: String
$type: OrderType!
) {
estimateOrder(
marketId: $marketId
partyId: $partyId
price: $price
size: $size
side: $side
timeInForce: $timeInForce
expiration: $expiration
type: $type
) {
fee {
makerFee
infrastructureFee
liquidityFee
}
marginLevels {
initialLevel
}
}
}
@@ -1,8 +0,0 @@
query MarketMarkPrice($marketId: ID!) {
market(id: $marketId) {
decimalPlaces
data {
markPrice
}
}
}
@@ -1,25 +0,0 @@
query MarketPositions($partyId: ID!) {
party(id: $partyId) {
id
accounts {
type
balance
asset {
decimals
}
market {
id
}
}
positionsConnection {
edges {
node {
openVolume
market {
id
}
}
}
}
}
}
@@ -1,28 +0,0 @@
query PartyMarketData($partyId: ID!) {
party(id: $partyId) {
id
accounts {
type
balance
asset {
id
decimals
}
market {
id
}
}
marginsConnection {
edges {
node {
market {
id
}
initialLevel
maintenanceLevel
searchLevel
}
}
}
}
}
@@ -10,7 +10,7 @@
export interface MarketMarkPrice_market_data {
__typename: "MarketData";
/**
* the mark price (actually an unsigned int)
* the mark price (an unsigned integer)
*/
markPrice: string;
}
@@ -19,7 +19,7 @@ export interface MarketMarkPrice_market {
__typename: "Market";
/**
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
* number denominated in the currency of the Market. (uint64)
* number denominated in the currency of the market. (uint64)
*
* Examples:
* Currency Balance decimalPlaces Real Balance
@@ -42,7 +42,7 @@ export interface MarketMarkPrice_market {
export interface MarketMarkPrice {
/**
* An instrument that is trading on the VEGA network
* An instrument that is trading on the Vega network
*/
market: MarketMarkPrice_market | null;
}
@@ -3,7 +3,7 @@
// @generated
// This file was automatically generated and should not be edited.
import { AccountType } from "./../../../../../../libs/types/src/__generated__/globalTypes";
import { AccountType } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: PartyMarketData
@@ -12,11 +12,11 @@ import { AccountType } from "./../../../../../../libs/types/src/__generated__/gl
export interface PartyMarketData_party_accounts_asset {
__typename: "Asset";
/**
* The id of the asset
* The ID of the asset
*/
id: string;
/**
* The precision of the asset
* The precision of the asset. Should match the decimal precision of the asset on its native chain, e.g: for ERC20 assets, it is often 18
*/
decimals: number;
}
@@ -64,15 +64,15 @@ export interface PartyMarketData_party_marginsConnection_edges_node {
*/
market: PartyMarketData_party_marginsConnection_edges_node_market;
/**
* this is the minimal margin required for a party to place a new order on the network (unsigned int actually)
* this is the minimum margin required for a party to place a new order on the network (unsigned integer)
*/
initialLevel: string;
/**
* minimal margin for the position to be maintained in the network (unsigned int actually)
* minimal margin for the position to be maintained in the network (unsigned integer)
*/
maintenanceLevel: string;
/**
* if the margin is between maintenance and search, the network will initiate a collateral search (unsigned int actually)
* if the margin is between maintenance and search, the network will initiate a collateral search (unsigned integer)
*/
searchLevel: string;
}
@@ -101,14 +101,14 @@ export interface PartyMarketData_party {
*/
accounts: PartyMarketData_party_accounts[] | null;
/**
* Margin level for a market
* Margin levels for a market
*/
marginsConnection: PartyMarketData_party_marginsConnection;
}
export interface PartyMarketData {
/**
* An entity that is trading on the VEGA network
* An entity that is trading on the Vega network
*/
party: PartyMarketData_party | null;
}
@@ -3,7 +3,7 @@
// @generated
// This file was automatically generated and should not be edited.
import { Side, OrderTimeInForce, OrderType } from "./../../../../../../libs/types/src/__generated__/globalTypes";
import { Side, OrderTimeInForce, OrderType } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: EstimateOrder
@@ -12,15 +12,15 @@ import { Side, OrderTimeInForce, OrderType } from "./../../../../../../libs/type
export interface EstimateOrder_estimateOrder_fee {
__typename: "TradeFee";
/**
* The maker fee, aggressive party to the other party (the one who had an order in the book)
* The maker fee, paid by the aggressive party to the other party (the one who had an order in the book)
*/
makerFee: string;
/**
* The infrastructure fee, a fee paid to the node runner to maintain the vega network
* The infrastructure fee, a fee paid to the validators to maintain the Vega network
*/
infrastructureFee: string;
/**
* The fee paid to the market makers to provide liquidity in the market
* The fee paid to the liquidity providers that committed liquidity to the market
*/
liquidityFee: string;
}
@@ -28,7 +28,7 @@ export interface EstimateOrder_estimateOrder_fee {
export interface EstimateOrder_estimateOrder_marginLevels {
__typename: "MarginLevels";
/**
* this is the minimal margin required for a party to place a new order on the network (unsigned int actually)
* this is the minimum margin required for a party to place a new order on the network (unsigned integer)
*/
initialLevel: string;
}
@@ -3,7 +3,7 @@
// @generated
// This file was automatically generated and should not be edited.
import { AccountType } from "./../../../../../../libs/types/src/__generated__/globalTypes";
import { AccountType } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: MarketPositions
@@ -12,7 +12,7 @@ import { AccountType } from "./../../../../../../libs/types/src/__generated__/gl
export interface MarketPositions_party_accounts_asset {
__typename: "Asset";
/**
* The precision of the asset
* The precision of the asset. Should match the decimal precision of the asset on its native chain, e.g: for ERC20 assets, it is often 18
*/
decimals: number;
}
@@ -96,7 +96,7 @@ export interface MarketPositions_party {
export interface MarketPositions {
/**
* An entity that is trading on the VEGA network
* An entity that is trading on the Vega network
*/
party: MarketPositions_party | null;
}
@@ -1,78 +0,0 @@
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type EstimateOrderQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
partyId: Types.Scalars['ID'];
price?: Types.InputMaybe<Types.Scalars['String']>;
size: Types.Scalars['String'];
side: Types.Side;
timeInForce: Types.OrderTimeInForce;
expiration?: Types.InputMaybe<Types.Scalars['String']>;
type: Types.OrderType;
}>;
export type EstimateOrderQuery = { __typename?: 'Query', estimateOrder: { __typename?: 'OrderEstimate', fee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string }, marginLevels: { __typename?: 'MarginLevels', initialLevel: string } } };
export const EstimateOrderDocument = gql`
query EstimateOrder($marketId: ID!, $partyId: ID!, $price: String, $size: String!, $side: Side!, $timeInForce: OrderTimeInForce!, $expiration: String, $type: OrderType!) {
estimateOrder(
marketId: $marketId
partyId: $partyId
price: $price
size: $size
side: $side
timeInForce: $timeInForce
expiration: $expiration
type: $type
) {
fee {
makerFee
infrastructureFee
liquidityFee
}
marginLevels {
initialLevel
}
}
}
`;
/**
* __useEstimateOrderQuery__
*
* To run a query within a React component, call `useEstimateOrderQuery` and pass it any options that fit your needs.
* When your component renders, `useEstimateOrderQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useEstimateOrderQuery({
* variables: {
* marketId: // value for 'marketId'
* partyId: // value for 'partyId'
* price: // value for 'price'
* size: // value for 'size'
* side: // value for 'side'
* timeInForce: // value for 'timeInForce'
* expiration: // value for 'expiration'
* type: // value for 'type'
* },
* });
*/
export function useEstimateOrderQuery(baseOptions: Apollo.QueryHookOptions<EstimateOrderQuery, EstimateOrderQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<EstimateOrderQuery, EstimateOrderQueryVariables>(EstimateOrderDocument, options);
}
export function useEstimateOrderLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EstimateOrderQuery, EstimateOrderQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<EstimateOrderQuery, EstimateOrderQueryVariables>(EstimateOrderDocument, options);
}
export type EstimateOrderQueryHookResult = ReturnType<typeof useEstimateOrderQuery>;
export type EstimateOrderLazyQueryHookResult = ReturnType<typeof useEstimateOrderLazyQuery>;
export type EstimateOrderQueryResult = Apollo.QueryResult<EstimateOrderQuery, EstimateOrderQueryVariables>;
@@ -1,51 +0,0 @@
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type MarketMarkPriceQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type MarketMarkPriceQuery = { __typename?: 'Query', market?: { __typename?: 'Market', decimalPlaces: number, data?: { __typename?: 'MarketData', markPrice: string } | null } | null };
export const MarketMarkPriceDocument = gql`
query MarketMarkPrice($marketId: ID!) {
market(id: $marketId) {
decimalPlaces
data {
markPrice
}
}
}
`;
/**
* __useMarketMarkPriceQuery__
*
* To run a query within a React component, call `useMarketMarkPriceQuery` and pass it any options that fit your needs.
* When your component renders, `useMarketMarkPriceQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useMarketMarkPriceQuery({
* variables: {
* marketId: // value for 'marketId'
* },
* });
*/
export function useMarketMarkPriceQuery(baseOptions: Apollo.QueryHookOptions<MarketMarkPriceQuery, MarketMarkPriceQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<MarketMarkPriceQuery, MarketMarkPriceQueryVariables>(MarketMarkPriceDocument, options);
}
export function useMarketMarkPriceLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketMarkPriceQuery, MarketMarkPriceQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<MarketMarkPriceQuery, MarketMarkPriceQueryVariables>(MarketMarkPriceDocument, options);
}
export type MarketMarkPriceQueryHookResult = ReturnType<typeof useMarketMarkPriceQuery>;
export type MarketMarkPriceLazyQueryHookResult = ReturnType<typeof useMarketMarkPriceLazyQuery>;
export type MarketMarkPriceQueryResult = Apollo.QueryResult<MarketMarkPriceQuery, MarketMarkPriceQueryVariables>;
@@ -1,68 +0,0 @@
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type MarketPositionsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type MarketPositionsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, accounts?: Array<{ __typename?: 'Account', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', decimals: number }, market?: { __typename?: 'Market', id: string } | null }> | null, positionsConnection: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', openVolume: string, market: { __typename?: 'Market', id: string } } }> | null } } | null };
export const MarketPositionsDocument = gql`
query MarketPositions($partyId: ID!) {
party(id: $partyId) {
id
accounts {
type
balance
asset {
decimals
}
market {
id
}
}
positionsConnection {
edges {
node {
openVolume
market {
id
}
}
}
}
}
}
`;
/**
* __useMarketPositionsQuery__
*
* To run a query within a React component, call `useMarketPositionsQuery` and pass it any options that fit your needs.
* When your component renders, `useMarketPositionsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useMarketPositionsQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function useMarketPositionsQuery(baseOptions: Apollo.QueryHookOptions<MarketPositionsQuery, MarketPositionsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<MarketPositionsQuery, MarketPositionsQueryVariables>(MarketPositionsDocument, options);
}
export function useMarketPositionsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketPositionsQuery, MarketPositionsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<MarketPositionsQuery, MarketPositionsQueryVariables>(MarketPositionsDocument, options);
}
export type MarketPositionsQueryHookResult = ReturnType<typeof useMarketPositionsQuery>;
export type MarketPositionsLazyQueryHookResult = ReturnType<typeof useMarketPositionsLazyQuery>;
export type MarketPositionsQueryResult = Apollo.QueryResult<MarketPositionsQuery, MarketPositionsQueryVariables>;
@@ -1,71 +0,0 @@
import * as Types from '@vegaprotocol/types/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type PartyMarketDataQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type PartyMarketDataQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, accounts?: Array<{ __typename?: 'Account', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string, decimals: number }, market?: { __typename?: 'Market', id: string } | null }> | null, marginsConnection: { __typename?: 'MarginConnection', edges?: Array<{ __typename?: 'MarginEdge', node: { __typename?: 'MarginLevels', initialLevel: string, maintenanceLevel: string, searchLevel: string, market: { __typename?: 'Market', id: string } } }> | null } } | null };
export const PartyMarketDataDocument = gql`
query PartyMarketData($partyId: ID!) {
party(id: $partyId) {
id
accounts {
type
balance
asset {
id
decimals
}
market {
id
}
}
marginsConnection {
edges {
node {
market {
id
}
initialLevel
maintenanceLevel
searchLevel
}
}
}
}
}
`;
/**
* __usePartyMarketDataQuery__
*
* To run a query within a React component, call `usePartyMarketDataQuery` and pass it any options that fit your needs.
* When your component renders, `usePartyMarketDataQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = usePartyMarketDataQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function usePartyMarketDataQuery(baseOptions: Apollo.QueryHookOptions<PartyMarketDataQuery, PartyMarketDataQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<PartyMarketDataQuery, PartyMarketDataQueryVariables>(PartyMarketDataDocument, options);
}
export function usePartyMarketDataLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PartyMarketDataQuery, PartyMarketDataQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<PartyMarketDataQuery, PartyMarketDataQueryVariables>(PartyMarketDataDocument, options);
}
export type PartyMarketDataQueryHookResult = ReturnType<typeof usePartyMarketDataQuery>;
export type PartyMarketDataLazyQueryHookResult = ReturnType<typeof usePartyMarketDataLazyQuery>;
export type PartyMarketDataQueryResult = Apollo.QueryResult<PartyMarketDataQuery, PartyMarketDataQueryVariables>;
@@ -1,124 +0,0 @@
import { MockedProvider } from '@apollo/client/testing';
import { renderHook } from '@testing-library/react';
import { Side } from '@vegaprotocol/types';
import type { Order } from '@vegaprotocol/orders';
import useCalculateSlippage from './use-calculate-slippage';
const mockData = {
decimalPlaces: 0,
positionDecimalPlaces: 0,
depth: {
buy: [
{
price: '5',
volume: '2',
},
{
price: '4',
volume: '3',
},
{
price: '3',
volume: '2',
},
{
price: '2',
volume: '1',
},
{
price: '1',
volume: '1',
},
],
sell: [
{
price: '6',
volume: '1',
},
{
price: '7',
volume: '3',
},
{
price: '8',
volume: '2',
},
{
price: '9',
volume: '1',
},
{
price: '10',
volume: '2',
},
],
},
};
let mockOrderBookData = {
data: mockData,
};
jest.mock('@vegaprotocol/market-depth', () => ({
...jest.requireActual('@vegaprotocol/market-depth'),
useOrderBookData: jest.fn(() => mockOrderBookData),
}));
describe('useCalculateSlippage Hook', () => {
describe('calculate proper result', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('long order', () => {
const { result } = renderHook(
() =>
useCalculateSlippage({
marketId: 'marketId',
order: { size: '10', side: Side.SIDE_BUY } as Order,
}),
{
wrapper: MockedProvider,
}
);
expect(result.current).toEqual('33.33');
});
it('short order', () => {
const { result } = renderHook(
() =>
useCalculateSlippage({
marketId: 'marketId',
order: { size: '10', side: Side.SIDE_SELL } as Order,
}),
{
wrapper: MockedProvider,
}
);
expect(result.current).toEqual('31.11');
});
it('when no order book result should be null', () => {
mockOrderBookData = {
data: {
...mockData,
depth: {
...mockData.depth,
buy: [],
},
},
};
const { result } = renderHook(
() =>
useCalculateSlippage({
marketId: 'marketId',
order: { size: '10', side: Side.SIDE_SELL } as Order,
}),
{
wrapper: MockedProvider,
}
);
expect(result.current).toBeNull();
});
});
});
@@ -1,62 +0,0 @@
import { useMemo } from 'react';
import { Side } from '@vegaprotocol/types';
import { useOrderBookData } from '@vegaprotocol/market-depth';
import type { Order } from '@vegaprotocol/orders';
import { BigNumber } from 'bignumber.js';
import { formatNumber, toBigNum } from '@vegaprotocol/react-helpers';
interface Props {
marketId: string;
order: Order;
}
const useCalculateSlippage = ({ marketId, order }: Props) => {
const variables = useMemo(() => ({ marketId }), [marketId]);
const { data } = useOrderBookData({
variables,
throttleMilliseconds: 5000,
});
const volPriceArr =
data?.depth[order.side === Side.SIDE_BUY ? 'sell' : 'buy'] || [];
if (volPriceArr.length) {
const decimals = data?.decimalPlaces ?? 0;
const positionDecimals = data?.positionDecimalPlaces ?? 0;
const bestPrice = toBigNum(volPriceArr[0].price, decimals);
const { size } = order;
let descSize = new BigNumber(size);
let i = 0;
const volPricePairs: Array<[BigNumber, BigNumber]> = [];
while (!descSize.isZero() && i < volPriceArr.length) {
const price = toBigNum(volPriceArr[i].price, decimals);
const amount = BigNumber.min(
descSize,
toBigNum(volPriceArr[i].volume, positionDecimals)
);
volPricePairs.push([price, amount]);
descSize = BigNumber.max(0, descSize.minus(amount));
i++;
}
if (volPricePairs.length) {
const volWeightAvPricePair = volPricePairs.reduce(
(agg, item) => {
agg[0] = agg[0].plus(item[0].multipliedBy(item[1]));
agg[1] = agg[1].plus(item[1]);
return agg;
},
[new BigNumber(0), new BigNumber(0)]
);
const volWeightAvPrice = volWeightAvPricePair[0].dividedBy(
volWeightAvPricePair[1]
);
const slippage = volWeightAvPrice
.minus(bestPrice)
.absoluteValue()
.dividedBy(bestPrice)
.multipliedBy(100);
return formatNumber(slippage, 2);
}
}
return null;
};
export default useCalculateSlippage;
@@ -22,7 +22,7 @@ const useColumnDefinitions = ({ isMobile }: Props) => {
headerName: t('Markets'),
headerClass: 'uppercase',
minWidth: isMobile ? 160 : 350,
field: 'tradableInstrument.instrument.name',
field: 'name',
cellClass: 'overflow-visible',
cellRenderer: ({ data }: { data: SimpleMarketsType }) => (
<MarketNameRenderer market={data} isMobile={isMobile} />
@@ -93,13 +93,13 @@ const useColumnDefinitions = ({ isMobile }: Props) => {
{
colId: 'status',
headerName: t('Status'),
field: 'state',
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-2 md:px-6 leading-4 md:leading-6">
{MARKET_STATES_MAP[data.state || '']}
<div className="border text-center px-2 md:px-8 leading-4 md:leading-6">
{MARKET_STATES_MAP[data.data?.market.state || '']}
</div>
</div>
),
@@ -112,7 +112,7 @@ const useColumnDefinitions = ({ isMobile }: Props) => {
width: isMobile ? 35 : 100,
cellRenderer: ({ data }: { data: SimpleMarkets_markets }) => (
<div className="h-full flex h-full items-center justify-end">
<div className="uppercase text-center pr-2">
<div className="uppercase text-center pr-8">
{!isMobile && t('Trade')}
<Icon
name={IconNames.ARROW_TOP_RIGHT}

Some files were not shown because too many files have changed in this diff Show More