Compare commits

..
Author SHA1 Message Date
Matthew Russell 915a1022ff fix: remove double border on header 2023-07-13 12:09:34 +01:00
Matthew Russell 867c0ddfae feat: drop sidebar below nav 2023-07-13 12:05:16 +01:00
Matthew Russell 8d01b4e50b feat: make sidebar align bottom for mobile 2023-07-13 11:52:56 +01:00
Matthew Russell ee288fa127 feat: market selector in main nav 2023-07-13 11:51:44 +01:00
Matthew Russell 503fbdf199 fix: disclaimer width 2023-07-13 11:51:43 +01:00
Matthew Russell 67c3f154fa chore: remove stray merge conflicts 2023-07-13 11:51:41 +01:00
Matthew Russell d3f8c9f8ac feat: change disclaimer to use max width so its responsive 2023-07-13 11:51:21 +01:00
Matthew Russell 6cad2501b8 feat: move settings back to sidebar 2023-07-13 11:51:20 +01:00
Matthew Russell ecb300ae7c feat: nav text color 2023-07-13 11:50:31 +01:00
Matthew Russell 800eaa2bbd feat: add navbar divider, fix navbar spacing 2023-07-13 11:50:31 +01:00
Matthew Russell 3eb81c57ea chore: adjust height of dropdowns 2023-07-13 11:50:31 +01:00
Matthew Russell 4069bf2a09 chore: remove unsupported theme prop 2023-07-13 11:50:31 +01:00
Matthew Russell e1def7aec9 feat: use dialog for mobile menu 2023-07-13 11:50:31 +01:00
Matthew Russell 48ecb3652c feat: add mobile wallet button logic 2023-07-13 11:50:31 +01:00
Matthew Russell 5173b4559e feat: nav changes for trading app 2023-07-13 11:50:30 +01:00
Matthew Russell 90cfe08f05 feat: tidy up dialogs 2023-07-13 11:48:43 +01:00
Matthew Russell c2d0e26c5a feat: update colors 2023-07-13 11:48:43 +01:00
Matthew Russell aa12d67bb1 feat: tidy up header styles 2023-07-13 11:48:43 +01:00
Matthew Russell 59dee33e47 feat: adjust tabs and table styles add cog 2023-07-13 11:48:43 +01:00
Matthew Russell 38c1b43225 feat: refactor routing and add sidebar 2023-07-13 11:48:43 +01:00
Matthew Russell 521b32d5f7 chore: move settings to bottom 2023-07-13 11:48:11 +01:00
Matthew Russell 8d72ae0842 chore: tweak dark grayscale 2023-07-13 11:44:21 +01:00
Matthew Russell 80cd14c47a chore: adjust cdark colors 2023-07-13 09:32:51 +01:00
Matthew Russell ce4637e03d test: fix settings test 2023-07-12 01:24:25 +01:00
Matthew Russell 96fd3ba0cb chore: re add init flag 2023-07-11 17:11:17 +01:00
Matthew Russell 88af5862df chore: make select only have additional padding to accomodate dropdown chevron 2023-07-11 16:20:37 +01:00
Matthew Russell e1c027cead chore: fix test after removal of side bar init flag 2023-07-11 14:34:13 +01:00
Matthew Russell a67459ac3e feat: remove init flag in sidebar store, use persist 2023-07-11 14:32:01 +01:00
Matthew Russell dd239fbd07 chore: fix build-spec 2023-07-11 14:32:01 +01:00
Matthew Russell d24ec424ac fix: transfer tests 2023-07-11 14:32:01 +01:00
Matthew Russell 588d1b7c47 chore: lint 2023-07-11 14:32:01 +01:00
Matthew Russell 5a815a188f feat: reduce size of chevron dropdown 2023-07-11 14:32:01 +01:00
Matthew Russell 6297f0b3e3 feat: adjust separators in header and sidebar 2023-07-11 14:32:01 +01:00
Matthew Russell 511b700d01 feat: adjust sidebar layout 2023-07-11 14:32:01 +01:00
Matthew Russell 370d51f66a chore: reduce dropdown spacing 2023-07-11 14:32:01 +01:00
Matthew Russell dd4149f48f chore: fix long text in default select element 2023-07-11 14:32:01 +01:00
Matthew Russell c81cf3f49f feat: adjust padding and add same scroll element for all sidebar children 2023-07-11 14:32:00 +01:00
Matthew Russell b9b18a3e63 chore: remove stray apostrophe in render 2023-07-11 14:31:18 +01:00
Matthew Russell 80f1f50e6f feat: align nav dropdowns 2023-07-11 14:31:18 +01:00
Matthew Russell 7cd508a816 feat: reduce tab font size 2023-07-11 14:31:18 +01:00
Matthew Russell 20ec3aeccc chore: disable some tests add wait 2023-07-11 14:31:18 +01:00
Matthew Russell 063cd8c786 test: disable flakey trades test and move mocks for transfer test 2023-07-11 14:31:18 +01:00
Matthew Russell 1d41041d03 chore: tidy up dstylesheet 2023-07-11 14:31:18 +01:00
Matthew Russell 702e7f75e5 chore: lint 2023-07-11 14:31:18 +01:00
Matthew Russell cae93e35c2 test: fix out of sync ethereum address with form 2023-07-11 14:31:18 +01:00
Matthew Russell f6418118cd test: e2e test updates 2023-07-11 14:31:18 +01:00
Matthew Russell 72457d16dd chore: lint 2023-07-11 14:31:18 +01:00
Matthew Russell 1170572828 test: fix node health tests 2023-07-11 14:31:18 +01:00
Matthew Russell 1d03eebd37 test: updates to ensure e2e tests run 2023-07-11 14:31:18 +01:00
Matthew Russell 6731e20bc0 feat: ensure floating buttons stay within their container 2023-07-11 14:31:18 +01:00
Matthew Russell 176452eec8 chore: lint 2023-07-11 14:31:18 +01:00
Matthew Russell f31a19e71d fix: ts errors in test files 2023-07-11 14:31:18 +01:00
Matthew Russell 51bc291821 chore: tidy up 2023-07-11 14:31:18 +01:00
Matthew Russell a8c5aebca0 test: add unit test for sidebar components 2023-07-11 14:31:18 +01:00
Matthew Russell 4b9371885b feat: re org layout so that all pages get navbar 2023-07-11 14:31:18 +01:00
Matthew Russell e713ff383e feat: update to use links from router config 2023-07-11 14:31:18 +01:00
Matthew Russell 763b2a0821 fix: remove padding from toast container so no clash with indicator 2023-07-11 14:31:18 +01:00
Matthew Russell 953a33b6a2 feat: add 0 delay duration for side bar buttons 2023-07-11 14:31:18 +01:00
Matthew Russell dab175bd99 feat: add ticket icon 2023-07-11 14:31:18 +01:00
Matthew Russell 80879ca7fa feat: spacing for liquidity and portfolio pages 2023-07-11 14:31:18 +01:00
Matthew Russell 71aac33291 feat: move settings cog to top 2023-07-11 14:31:18 +01:00
Matthew Russell 6f9624589f feat: move sidebar to right 2023-07-11 14:31:18 +01:00
Matthew Russell aa2b080635 feat: adjust table overlay bg color 2023-07-11 14:31:18 +01:00
Matthew Russell b90b2c91c2 feat: adjust market selector 2023-07-11 14:31:17 +01:00
Matthew Russell 8ad3f09e4a feat: adjust padding of sidebar content 2023-07-11 14:31:17 +01:00
Matthew Russell a34d417c6f feat: add notification for transfer form 2023-07-11 14:31:17 +01:00
Matthew Russell b70a874244 feat: make sidebar open by default for trading/portfolio 2023-07-11 14:31:17 +01:00
Matthew Russell c2b1533d33 feat: add titles to some sidebars 2023-07-11 14:31:17 +01:00
Matthew Russell bdb5dc93e1 feat: tidy up dialogs 2023-07-11 14:31:17 +01:00
Matthew Russell b14a956a4d chore: adjust layout to work for mobile 2023-07-11 14:31:17 +01:00
Matthew Russell c0978da7f9 feat: add rounded border for panes 2023-07-11 14:31:17 +01:00
Matthew Russell 8c0a36f368 feat: adjust tab indicator 2023-07-11 14:31:17 +01:00
Matthew Russell 2ab93be3cd feat: add console tooltip for sidebar 2023-07-11 14:31:17 +01:00
Matthew Russell 7919c4a7ec feat: update colors 2023-07-11 14:31:17 +01:00
Matthew Russell 341a89bb4c feat: set default view for trading and portfolio page 2023-07-11 14:31:17 +01:00
Matthew Russell c9ce8c4188 test: fix market selector tests 2023-07-11 14:31:17 +01:00
Matthew Russell 6e6ddcce32 test: fix node health tests 2023-07-11 14:31:17 +01:00
Matthew Russell ea29c53f89 feat: adjust node health text size 2023-07-11 14:31:17 +01:00
Matthew Russell 3d82603be1 feat: adjust settings page to work for sidebar 2023-07-11 14:31:17 +01:00
Matthew Russell 7f34e95cb8 feat: adjust padding in sidebar, colors for chart controls 2023-07-11 14:31:17 +01:00
Matthew Russell 18be213431 feat: make appName and therefore logo for navigation component optional 2023-07-11 14:31:17 +01:00
Matthew Russell 63921442da chore: re add logo for now 2023-07-11 14:31:17 +01:00
Matthew Russell abe8341667 feat: adjust styles for small screens and add sidebar as overlay 2023-07-11 14:31:17 +01:00
Matthew Russell 6939723d37 feat: tidy up header styles 2023-07-11 14:31:15 +01:00
Matthew Russell 5af7ee5685 feat: add spacing to portfolio page 2023-07-11 14:30:38 +01:00
Matthew Russell 48a272f984 feat: add market info 2023-07-11 14:30:38 +01:00
Matthew Russell 901c045158 feat: adjust tabs and table styles add cog 2023-07-11 14:30:37 +01:00
Matthew Russell 81d5f6e0da chore: re add home to serve as redirect to default market 2023-07-11 14:28:19 +01:00
Matthew Russell 5b1dfddbff feat: adapt market-selector for popover 2023-07-11 14:28:19 +01:00
Matthew Russell 8c14b7b357 chore: keep using dialogs along side sidebar for now 2023-07-11 14:28:19 +01:00
Matthew Russell 03cc125f03 feat: open transfer sidebar, adjust trade-grid 2023-07-11 14:28:19 +01:00
Matthew Russell b35692a24d feat: refactor routing and add sidebar 2023-07-11 14:28:19 +01:00
226 changed files with 2004 additions and 4545 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ name: 'Add Issues To Project Board'
types:
- opened
env:
GH_TOKEN: ${{ secrets.PROJECT_MANAGE_ACTION }}
GH_TOKEN: ${{ secrets.GH_NEW_CARD_TO_PROJECT }}
PROJECT_ID: ${{ secrets.FRONT_END_PROJECT_ID }}
ISSUE_ID: ${{ github.event.issue.node_id }}
USER: ${{ github.actor }}
+8 -31
View File
@@ -5,7 +5,10 @@ on:
branches:
- release/*
- develop
pull_request:
- main
# uncomment pull_request and comment pull_request_target to test CI changes against feature branch not target branch (develop)
# pull_request:
pull_request_target:
types:
- opened
- ready_for_review
@@ -46,7 +49,7 @@ jobs:
lint-pr-title:
needs: node-modules
if: ${{ github.event_name == 'pull_request' }}
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
name: Verify PR title
uses: ./.github/workflows/lint-pr.yml
secrets: inherit
@@ -107,7 +110,6 @@ jobs:
echo "NX_HEAD: ${{ env.NX_HEAD }}"
echo "Affected: ${affected}"
echo "Branch slug: ${branch_slug}"
echo "Current ref: ${{ github.ref }}"
echo ">>>> eof debug"
projects_array=()
@@ -178,31 +180,6 @@ jobs:
fi
fi
# if branch starts with release/ and ends with trading / governance or explorer - overwrite the array of affected projects with fixed single application
if [[ "${{ github.ref }}" == *release* ]]; then
echo ">> This is a relase branch"
case "${{ github.ref }}" in
*trading)
echo ">> Only trading will be deployed"
projects_array=(trading)
projects_e2e_array=(trading)
;;
*governance)
echo ">> Only governance will be deployed"
projects_array=(governance)
projects_e2e_array=(governance)
;;
*explorer)
echo ">> Only explorer will be deployed"
projects_array=(explorer)
projects_e2e_array=(explorer)
;;
*)
echo ">> All apps will be deployed"
;;
esac
fi
echo "Projects: ${projects_array[@]}"
echo "Projects E2E: ${projects_e2e_array[@]}"
projects_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_array[@]}")
@@ -237,7 +214,7 @@ jobs:
publish-dist:
needs: lint-test-build
name: '(CD) publish dist'
if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'vegaprotocol/frontend-monorepo') || github.event_name == 'push' }}
# if: ${{ needs.lint-test-build.outputs.projects != '[]' }}
uses: ./.github/workflows/publish-dist.yml
secrets: inherit
with:
@@ -248,7 +225,7 @@ jobs:
needs:
- publish-dist
- lint-test-build
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'vegaprotocol/frontend-monorepo' }}
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
timeout-minutes: 60
name: '(CD) comment preview links'
steps:
@@ -294,7 +271,7 @@ jobs:
with:
issue-number: ${{ github.event.pull_request.number }}
body: |
Previews
Previews:
* governance: ${{ needs.lint-test-build.outputs.preview_governance }}
* explorer: ${{ needs.lint-test-build.outputs.preview_explorer }}
* trading: ${{ needs.lint-test-build.outputs.preview_trading }}
-59
View File
@@ -1,59 +0,0 @@
name: console-test-run
on:
workflow_call:
inputs:
github-sha:
required: true
type: string
jobs:
console-test:
timeout-minutes: 5
runs-on: self-hosted-runner
steps:
- name: Checkout console test repo
uses: actions/checkout@v3
with:
repository: vegaprotocol/console-test
path: './console-test'
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10.11'
- name: Install Poetry
uses: snok/install-poetry@v1
- name: Install dependencies
run: |
poetry install --no-root
working-directory: ./console-test
- name: load Binaries
run: |
poetry run python -m vega_sim.tools.load_binaries
working-directory: ./console-test
- name: pull console
run: |
poetry run docker pull ghcr.io/vegaprotocol/frontend/trading:${{ inputs.github-sha }}
- name: Update container_name in config.py
run: |
sed -i "s/container_name = \".*\"/container_name = \"vegaprotocol\/frontend\/trading:${{ inputs.github-sha }}\"/g" config.py
- name: install playwright
run: poetry run playwright install
working-directory: ./console-test
- name: run tests
run: poetry run pytest --numprocesses auto
working-directory: ./console-test
- name: Upload Playwright Trace
uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-trace
path: ./traces/
retention-days: 15
+21 -54
View File
@@ -22,39 +22,6 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Init variables
run: |
echo IS_PR=false >> $GITHUB_ENV
echo IS_MAINNET_RELEASE=false >> $GITHUB_ENV
echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
echo IS_S3_RELEASE=false >> $GITHUB_ENV
- name: Is PR
if: ${{ github.event_name == 'pull_request' }}
run: |
echo IS_PR=true >> $GITHUB_ENV
- name: Is mainnet release
if: ${{ contains(github.ref, 'release/mainnnet') && !contains(github.ref, 'mirror') }}
run: |
echo IS_MAINNET_RELEASE=true >> $GITHUB_ENV
- name: Is testnet release
if: ${{ contains(github.ref, 'release/testnet') }}
run: |
echo IS_TESTNET_RELEASE=true >> $GITHUB_ENV
- name: Is IPFS Release
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( env.IS_MAINNET_RELEASE == 'true' || env.IS_TESTNET_RELEASE == 'true' ) }}
run: |
echo IS_IPFS_RELEASE=true >> $GITHUB_ENV
- name: Is S3 Release
if: ${{ env.IS_IPFS_RELEASE == 'false' && github.event_name == 'push' }}
run: |
echo IS_S3_RELEASE=true >> $GITHUB_ENV
- name: Set up QEMU
id: quemu
uses: docker/setup-qemu-action@v2
@@ -66,7 +33,7 @@ jobs:
uses: docker/setup-buildx-action@v2
- name: Log in to the Container registry (ghcr)
if: ${{ env.IS_PR == 'true' }}
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
uses: docker/login-action@v2
with:
registry: ghcr.io
@@ -75,8 +42,9 @@ jobs:
- name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
with:
# registry: registry.hub.docker.com
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -102,8 +70,7 @@ jobs:
bucketName=''
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
# remove prefixing release/ and take the first string limited by - which is supposed to be name of the environment for releasing (format: release/testnet-trading)
envName="$(echo ${{ github.ref }} | sed -e "s|refs/heads/release/||" | cut -d '-' -f 1 )"
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1"
if [[ "${{ matrix.app }}" = "multisig-signer" ]]; then
@@ -118,7 +85,7 @@ jobs:
envName="mainnet"
bucketName="ui.vega.rocks"
fi
elif [[ "${{ github.ref }}" =~ .*mainnet$ ]]; then
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
envName="mainnet"
fi
@@ -178,7 +145,7 @@ jobs:
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
- name: Image digest
if: ${{ env.IS_PR == 'true' }}
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
run: echo ${{ steps.docker_build.outputs.digest }}
- name: Sanity check docker image
@@ -193,7 +160,7 @@ jobs:
uses: docker/build-push-action@v3
continue-on-error: true
id: ghcr-push
if: ${{ env.IS_PR == 'true' }}
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
@@ -208,7 +175,7 @@ jobs:
uses: docker/build-push-action@v3
continue-on-error: true
id: dockerhub-push
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
@@ -218,7 +185,7 @@ jobs:
ENV_NAME=${{ env.ENV_NAME }}
tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }}
vegaprotocol/${{ matrix.app }}:${{ endsWith(github.ref, 'main') && 'mainnet' || endsWith(github.ref, 'release/testnet') && 'testnet' || '' }}
- name: Publish dist as docker image (ghcr - retry)
uses: docker/build-push-action@v3
@@ -245,13 +212,13 @@ jobs:
ENV_NAME=${{ env.ENV_NAME }}
tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }}
vegaprotocol/${{ matrix.app }}:${{ endsWith(github.ref, 'main') && 'mainnet' || endsWith(github.ref, 'release/testnet') && 'testnet' || '' }}
# bucket creation in github.com/vegaprotocol/terraform//frontend
- name: Publish dist to s3
uses: jakejarvis/s3-sync-action@master
# s3 releases are not happening for trading on mainnet - it's IPFS
if: ${{ env.IS_S3_RELEASE == 'true' }}
if: ${{ github.event_name == 'push' && ( matrix.app != 'trading' || (matrix.app == 'trading' && !( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) ) ) }}
with:
args: --acl private --follow-symlinks --delete
env:
@@ -262,11 +229,11 @@ jobs:
SOURCE_DIR: 'dist-result'
- name: Install aws CLI
if: ${{ env.IS_S3_RELEASE == 'true' }}
if: ${{ github.event_name == 'push' && ( matrix.app != 'trading' || (matrix.app == 'trading' && !( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) ) ) }}
uses: unfor19/install-aws-cli-action@master
- name: Perform cache invalidation
if: ${{ env.IS_S3_RELEASE == 'true' }}
if: ${{ github.event_name == 'push' && ( matrix.app != 'trading' || (matrix.app == 'trading' && !( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) ) ) }}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -279,16 +246,16 @@ jobs:
- name: Add preview label
uses: actions-ecosystem/action-add-labels@v1
if: ${{ env.IS_PR == 'true' }}
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
with:
labels: ${{ matrix.app }}-preview
number: ${{ github.event.number }}
- name: Trigger fleek deployment
# release to ipfs happens only on mainnet (represented by main branch) for trading
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
run: |
if [[ "${{ env.IS_MAINNET_RELEASE }}" = "true" ]]; then
if echo ${{ github.ref }} | grep -q main; then
# display info about app
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \
@@ -301,7 +268,7 @@ jobs:
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
https://api.fleek.co/graphql
elif [[ "${{ env.IS_TESTNET_RELEASE }}" = "true" ]]; then
elif echo ${{ github.ref }} | grep -q release/testnet; then
# display info about app
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \
@@ -316,7 +283,7 @@ jobs:
fi
- name: Check out ipfs-redirect
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'testnet') ) }}
uses: actions/checkout@v3
with:
repository: 'vegaprotocol/ipfs-redirect'
@@ -325,7 +292,7 @@ jobs:
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
- name: Update interstitial page to point to the new console
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'testnet') ) }}
env:
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
run: |
@@ -347,11 +314,11 @@ jobs:
git config --global user.name "vega-ci-bot"
# update CID files
if [[ "${{ env.IS_MAINNET_RELEASE }}" = "true" ]]; then
if echo ${{ github.ref }} | grep -q main; then
echo $new_hash > cidv0-mainnet.txt
echo $new_cid > cidv1-mainnet.txt
git add cidv0-mainnet.txt cidv1-mainnet.txt
elif [[ "${{ env.IS_TESTNET_RELEASE }}" = "true" ]]; then
elif echo ${{ github.ref }} | grep -q release/testnet; then
echo $new_hash > cidv0-fairground.txt
echo $new_cid > cidv1-fairground.txt
git add cidv0-fairground.txt cidv1-fairground.txt
@@ -40,7 +40,7 @@ context.skip('Transactions page', function () {
.first()
.click({ force: true });
} else {
cy.log('Unable to find any transactions on page');
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
@@ -78,7 +78,7 @@ context.skip('Transactions page', function () {
}
});
} else {
cy.log('Unable to find any transactions on page');
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
+1 -1
View File
@@ -4,7 +4,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
NX_VEGA_ENV=MAINNET_MIRROR
NX_VEGA_ENV=MAINNET-MIRROR
NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.rocks/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
@@ -137,7 +137,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
hide: window.innerWidth <= BREAKPOINT_MD,
headerName: t('Enactment date'),
field: 'terms.enactmentDatetime',
valueFormatter: ({
valueFormatte: ({
value,
}: VegaValueFormatterParams<
ProposalListFieldsFragment,
@@ -1,52 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import type { components } from '../../../../../types/explorer';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
export interface TxDetailsOrderIcebergDetailsProps {
iceberg: components['schemas']['v1IcebergOpts'];
size: components['schemas']['v1OrderSubmission']['size'];
}
/**
.iceberg .sub {
vertical-align: bottom;
color: #954F01;
}
.iceberg .sup {
vertical-align: top;
color: #515E1E;
}
*/
/**
* Visualises the iceberg details of an order, showing the minimum visible size,
* the full size and the peak size.
*/
export const TxOrderIcebergDetails = ({
iceberg,
size,
}: TxDetailsOrderIcebergDetailsProps) => {
return (
<div
className="inline-block iceberg bg-vega-blue-300 border-vega-blue-350 border-[1px] text-[70%] font-mono py-[2px] pl-2 pr-3 height-[1em] leading-[1em] cursor-pointer"
style={{ clipPath: 'polygon(10% 0%, 100% 0%, 90% 100%, 0% 100%)' }}
>
<Tooltip description={t('Iceberg: Minimum visible size')}>
<span className="align-bottom text-vega-orange-650">
{iceberg.minimumVisibleSize || '-'}
</span>
</Tooltip>
<Tooltip description={t('Iceberg: Total size')}>
<span className="text-sm text-vega-blue-600 mx-3">{size}</span>
</Tooltip>
<Tooltip description={t('Iceberg: Visible peak')}>
<span className="align-top text-vega-yellow-600">
{iceberg.peakSize || '-'}
</span>
</Tooltip>
</div>
);
};
@@ -8,7 +8,6 @@ import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
import DeterministicOrderDetails from '../../order-details/deterministic-order-details';
import Hash from '../../links/hash';
import { TxOrderPeggedReferenceRow } from './order/tx-order-peg';
import { TxOrderIcebergDetails } from './order/tx-order-iceberg';
interface TxDetailsOrderProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -33,8 +32,6 @@ export const TxDetailsOrder = ({
const marketId = txData.command.orderSubmission.marketId || '-';
const reference = txData.command.orderSubmission.peggedOrder;
const side = txData.command.orderSubmission.side;
const iceberg = txData.command.orderSubmission.icebergOpts;
const size = txData.command.orderSubmission.size;
let deterministicId = '';
@@ -77,14 +74,6 @@ export const TxDetailsOrder = ({
marketId={marketId}
/>
) : null}
{iceberg ? (
<TableRow modifier="bordered">
<TableCell>{t('Iceberg details')}</TableCell>
<TableCell>
<TxOrderIcebergDetails iceberg={iceberg} size={size} />
</TableCell>
</TableRow>
) : null}
</TableWithTbody>
{deterministicId.length > 0 ? (
@@ -166,7 +166,6 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-250';
// This will get unwieldy and should probably produce a different colour of tag
// Note that colours are currently arbitrary
if (type === 'Chain Event' && !!command?.chainEvent) {
type = getLabelForChainEvent(command.chainEvent);
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
@@ -178,9 +177,6 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
type = getLabelForProposal(command.proposalSubmission);
}
colours = 'text-black bg-vega-yellow';
} else if (type === 'Order' && command) {
type = getLabelForOrderType(orderType, command);
colours = 'text-white dark-text-white bg-vega-blue dark:bg-vega-blue';
}
if (type === 'Vote on Proposal') {
@@ -1,5 +1,7 @@
import { Link, useParams } from 'react-router-dom';
import { DATA_SOURCES } from '../../../config';
import { getDateTimeFormat } from '@vegaprotocol/utils';
import type { TendermintBlocksResponse } from '../tendermint-blocks-response';
import { RouteTitle } from '../../../components/route-title';
import { TimeAgo } from '../../../components/time-ago';
import {
@@ -12,7 +14,7 @@ import { TxsPerBlock } from '../../../components/txs/txs-per-block';
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
import { Routes } from '../../route-names';
import { t } from '@vegaprotocol/i18n';
import { useBlockInfo } from '@vegaprotocol/tendermint';
import { useFetch } from '@vegaprotocol/react-helpers';
import { NodeLink } from '../../../components/links';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import EmptyList from '../../../components/empty-list/empty-list';
@@ -22,7 +24,10 @@ const Block = () => {
useDocumentTitle(['Blocks', `Block #${block}`]);
const {
state: { data: blockData, loading, error },
} = useBlockInfo(Number(block));
} = useFetch<TendermintBlocksResponse>(
`${DATA_SOURCES.tendermintUrl}/block?height=${block}`,
{ cache: 'force-cache' }
);
return (
<section>
@@ -54,7 +54,7 @@ export const PartyBlockStake = ({
{p?.stakingSummary.currentStakeAvailable ? (
<KeyValueTable>
<KeyValueTableRow noBorder={true}>
<div>{t('Associated to key')}</div>
<div>{t('Available stake')}</div>
<div>
<GovernanceAssetBalance
price={p.stakingSummary.currentStakeAvailable}
@@ -62,7 +62,7 @@ export const PartyBlockStake = ({
</div>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<div>{t('Staked to validator')}</div>
<div>{t('Active stake')}</div>
<div>
<GovernanceAssetBalance price={linkedStake || '0'} />
</div>
@@ -67,7 +67,7 @@ const Party = () => {
text: t('Go back'),
action: () => navigate(-1),
className: 'py-1',
size: 'small',
size: 'sm',
}}
/>
</div>
@@ -320,8 +320,8 @@ context(
// 3001-VOTE-076
cy.getByTestId(connectToVegaWalletButton)
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
cy.getByTestId(connectToVegaWalletButton).click();
.and('have.text', 'Connect Vega wallet')
.click();
cy.getByTestId('connector-jsonRpc').click();
cy.getByTestId(vegaWalletNameElement).should('be.visible');
cy.getByTestId(connectToVegaWalletButton).should('not.exist');
@@ -161,7 +161,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
cy.getByTestId('menu-drawer').should('be.visible');
});
it('should have link for proposal page', function () {
it.skip('should have link for proposal page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/proposals"]')
.should('exist')
@@ -1,12 +1,13 @@
/// <reference types="cypress" />
import {
navigateTo,
navigation,
turnTelemetryOff,
waitForSpinner,
} from '../../support/common.functions';
import {
createTenDigitUnixTimeStampForSpecifiedDays,
enterRawProposalBody,
enterUniqueFreeFormProposalBody,
goToMakeNewProposal,
governanceProposalType,
} from '../../support/governance.functions';
@@ -46,11 +47,12 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
.and('contain.text', 'USDC (fake)');
});
it('Unable to submit proposal with public key', function () {
it.skip('Unable to submit proposal with public key', function () {
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
goToMakeNewProposal(governanceProposalType.RAW);
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(8));
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('50', 'pub key proposal test');
cy.getByTestId('dialog-content')
.first()
.within(() => {
@@ -78,7 +78,7 @@ context(
cy.getByTestId('connector-jsonRpc')
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
cy.getByTestId('connector-rest')
cy.getByTestId('connector-hosted')
.should('be.visible')
.and('have.text', 'Hosted Fairground wallet');
});
@@ -94,7 +94,7 @@ context(
describe('when rest connector form opened', function () {
before('click hosted wallet app button', function () {
cy.getByTestId(connectorsList).within(() => {
cy.getByTestId('connector-rest').click();
cy.getByTestId('connector-hosted').click();
});
});
-4
View File
@@ -18,10 +18,6 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
#Test configuration variables
CYPRESS_FAIRGROUND=false
LC_ALL="en_US.UTF-8"
-3
View File
@@ -19,9 +19,6 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
NX_TENDERMINT_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
#Test configuration variables
CYPRESS_FAIRGROUND=false
-3
View File
@@ -13,6 +13,3 @@ NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
-3
View File
@@ -14,6 +14,3 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
+2 -5
View File
@@ -1,8 +1,8 @@
# App configuration variables
NX_VEGA_ENV=MAINNET_MIRROR
NX_VEGA_ENV=MAINNET-MIRROR
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz","MAINNET_MIRROR":"https://governance.mainnet-mirror.vega.rocks","STAGNET1":"https://trading.stagnet1.vega.rocks"}'
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz","MAINNET-MIRROR":"https://governance.mainnet-mirror.vega.rocks","STAGNET1":"https://trading.stagnet1.vega.rocks"}'
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
@@ -13,6 +13,3 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
-3
View File
@@ -9,6 +9,3 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
-3
View File
@@ -14,6 +14,3 @@ NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
-3
View File
@@ -11,6 +11,3 @@ NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
@@ -2,8 +2,13 @@ import { Button } from '@vegaprotocol/ui-toolkit';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
export const ConnectToVega = () => {
const { appDispatch } = useAppState();
const { t } = useTranslation();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
@@ -11,6 +16,10 @@ export const ConnectToVega = () => {
return (
<Button
onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog();
}}
data-testid="connect-to-vega-wallet-btn"
+1 -12
View File
@@ -18,10 +18,6 @@ import { VegaWallet } from '../vega-wallet';
import { useLocation, useMatch } from 'react-router-dom';
import { useEffect } from 'react';
import { useTelemetryDialog } from '../telemetry-dialog/telemetry-dialog';
import {
ProtocolUpgradeCountdown,
ProtocolUpgradeCountdownMode,
} from '@vegaprotocol/proposals';
export const SettingsLink = () => {
const { open, isOpen, close } = useTelemetryDialog();
@@ -65,14 +61,7 @@ export const Nav = ({ theme }: Pick<NavigationProps, 'theme'>) => {
appName="Governance"
theme={theme}
breakpoints={[458, 959]}
actions={
<>
<SettingsLink />
<ProtocolUpgradeCountdown
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
</>
}
actions={<SettingsLink />}
>
<NavigationList
className="[.drawer-content_&]:border-b [.drawer-content_&]:border-b-vega-light-200 dark:[.drawer-content_&]:border-b-vega-dark-200 [.drawer-content_&]:pb-8 [.drawer-content_&]:mb-2"
@@ -4,11 +4,7 @@ import type { ReactNode } from 'react';
import { AnnouncementBanner } from '@vegaprotocol/announcements';
import { Nav } from '../nav';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import {
ProtocolUpgradeCountdownMode,
ProtocolUpgradeProposalNotification,
} from '@vegaprotocol/proposals';
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
import React from 'react';
interface AppLayoutProps {
children: ReactNode;
@@ -35,23 +31,8 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
/>
)}
<Nav theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
<NotificationsContainer />
</div>
<div className={AppLayoutClasses}>{children}</div>
</div>
);
};
const NotificationsContainer = () => {
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
return (
<div data-testid="banners">
<ProtocolUpgradeProposalNotification
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
{isReadOnly ? (
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
) : null}
</div>
);
};
@@ -1,3 +1,5 @@
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import React from 'react';
export interface TemplateSidebarProps {
@@ -6,18 +8,24 @@ export interface TemplateSidebarProps {
}
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
return (
<div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[1fr] lg:grid-cols-[1fr_450px]">
<main className="max-w-[100vw] col-start-1 p-4 overflow-auto">
{children}
</main>
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
{sidebar.map((Component, i) => (
<section className="mb-4 last:mb-0" key={i}>
{Component}
</section>
))}
</aside>
</div>
<>
{isReadOnly ? (
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
) : null}
<div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[1fr] lg:grid-cols-[1fr_450px]">
<main className="max-w-[100vw] col-start-1 p-4 overflow-auto">
{children}
</main>
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
{sidebar.map((Component, i) => (
<section className="mb-4 last:mb-0" key={i}>
{Component}
</section>
))}
</aside>
</div>
</>
);
}
@@ -3,6 +3,11 @@ import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
interface VegaWalletContainerProps {
children: (key: string) => React.ReactElement;
}
@@ -10,6 +15,7 @@ interface VegaWalletContainerProps {
export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
const { t } = useTranslation();
const { pubKey } = useVegaWallet();
const { appDispatch } = useAppState();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
@@ -19,6 +25,10 @@ export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
<Button
data-testid="connect-to-vega-wallet-btn"
onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog();
}}
>
@@ -13,6 +13,12 @@ export const VegaWalletDialogs = () => {
<>
<VegaConnectDialog
connectors={Connectors}
onChangeOpen={(open) =>
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: open,
})
}
riskMessage={<RiskMessage />}
/>
@@ -71,6 +71,7 @@ export const VegaWallet = () => {
const VegaWalletNotConnected = () => {
const { t } = useTranslation();
const { appDispatch } = useAppState();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
@@ -78,6 +79,10 @@ const VegaWalletNotConnected = () => {
<>
<Button
onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog();
}}
fill={true}
-4
View File
@@ -25,10 +25,6 @@ export const ContractAddresses: {
claimAddress: customClaimAddress ?? '0x0',
lockedAddress: customLockedAddress ?? '0x0',
},
MAINNET_MIRROR: {
claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
lockedAddress: '0x0', // TODO not deployed to this env
},
DEVNET: {
claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
lockedAddress: '0x0', // TODO not deployed to this env
@@ -28,6 +28,9 @@ export interface AppState {
/** Total number of VEGA Tokens, both vesting and unlocked, associated for staking */
totalAssociated: BigNumber;
/** Whether or not the connect to VEGA wallet overlay is open */
vegaWalletOverlay: boolean;
/** Whether or not the manage VEGA wallet overlay is open */
vegaWalletManageOverlay: boolean;
@@ -49,7 +52,9 @@ export enum AppStateActionType {
SET_TOKEN,
SET_ALLOWANCE,
REFRESH_BALANCES,
SET_VEGA_WALLET_OVERLAY,
SET_VEGA_WALLET_MANAGE_OVERLAY,
SET_DRAWER,
REFRESH_ASSOCIATED_BALANCES,
SET_ASSOCIATION_BREAKDOWN,
SET_TRANSACTION_OVERLAY,
@@ -64,10 +69,18 @@ export type AppStateAction =
totalSupply: BigNumber;
totalAssociated: BigNumber;
}
| {
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY;
isOpen: boolean;
}
| {
type: AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY;
isOpen: boolean;
}
| {
type: AppStateActionType.SET_DRAWER;
isOpen: boolean;
}
| {
type: AppStateActionType.SET_TRANSACTION_OVERLAY;
isOpen: boolean;
@@ -14,6 +14,7 @@ const initialAppState: AppState = {
totalAssociated: new BigNumber(0),
decimals: 0,
totalSupply: new BigNumber(0),
vegaWalletOverlay: false,
vegaWalletManageOverlay: false,
transactionOverlay: false,
bannerMessage: '',
@@ -30,10 +31,23 @@ function appStateReducer(state: AppState, action: AppStateAction): AppState {
totalAssociated: action.totalAssociated,
};
}
case AppStateActionType.SET_VEGA_WALLET_OVERLAY: {
return {
...state,
vegaWalletOverlay: action.isOpen,
};
}
case AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY: {
return {
...state,
vegaWalletManageOverlay: action.isOpen,
vegaWalletOverlay: action.isOpen ? false : state.vegaWalletOverlay,
};
}
case AppStateActionType.SET_DRAWER: {
return {
...state,
vegaWalletOverlay: false,
};
}
case AppStateActionType.SET_TRANSACTION_OVERLAY: {
@@ -837,7 +837,5 @@
"RejectedProposals": "Rejected proposals",
"networkGovernance": "Network governance",
"networkUpgrades": "Network upgrades",
"assetSpecification": "Asset specification",
"Estimated time to upgrade": "Estimated time to upgrade",
"Upgraded at": "Upgraded at"
"assetSpecification": "Asset specification"
}
@@ -2,18 +2,15 @@ import {
RestConnector,
JsonRpcConnector,
ViewConnector,
InjectedConnector,
} from '@vegaprotocol/wallet';
const urlParams = new URLSearchParams(window.location.search);
export const injected = new InjectedConnector();
export const rest = new RestConnector();
export const jsonRpc = new JsonRpcConnector();
export const view = new ViewConnector(urlParams.get('address'));
export const Connectors = {
injected,
rest,
jsonRpc,
view,
@@ -31,7 +31,6 @@ import {
orderByDate,
orderByUpgradeBlockHeight,
} from '../proposals/components/proposals-list/proposals-list';
import { BigNumber } from '../../lib/bignumber';
const nodesToShow = 6;
@@ -249,8 +248,6 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
const activeNodes = removePaginationWrapper(
validatorsData?.nodesConnection.edges
).filter((node) =>
new BigNumber(node.rankingScore.performanceScore).isGreaterThan(0)
);
const trimmedActiveNodes = activeNodes?.slice(0, nodesToShow);
@@ -4,20 +4,16 @@ import { render } from '@testing-library/react';
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
import { ProtocolUpgradeProposalDetailInfo } from './protocol-upgrade-proposal-detail-info';
const PROPOSAL = {
vegaReleaseTag: 'v0.1.234',
status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
upgradeBlockHeight: '12345',
approvers: [],
};
const renderComponent = (lastBlockHeight?: string, time?: string) =>
const renderComponent = () =>
render(
<ProtocolUpgradeProposalDetailInfo
proposal={PROPOSAL}
lastBlockHeight={lastBlockHeight}
time={time}
proposal={{
vegaReleaseTag: 'v0.1.234',
status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
upgradeBlockHeight: '12345',
approvers: [],
}}
/>
);
@@ -47,28 +43,4 @@ describe('ProtocolUpgradeProposalDetailInfo', () => {
'v0.1.234'
);
});
it('should display estimated time if proposal is still pending', () => {
const { getByTestId } = renderComponent('12', 'time');
expect(getByTestId('protocol-upgrade-time-label')).toHaveTextContent(
'Estimated time to upgrade'
);
expect(getByTestId('protocol-upgrade-time')).toHaveTextContent('time');
});
it('should display upgraded at if proposal is done', () => {
const { getByTestId } = renderComponent('123456', 'time');
expect(getByTestId('protocol-upgrade-time-label')).toHaveTextContent(
'Upgraded at'
);
expect(getByTestId('protocol-upgrade-time')).toHaveTextContent('time');
});
it('should not display time if none provided', () => {
const { queryByTestId } = renderComponent('123456', undefined);
expect(
queryByTestId('protocol-upgrade-time-label')
).not.toBeInTheDocument();
expect(queryByTestId('protocol-upgrade-time')).not.toBeInTheDocument();
});
});
@@ -5,33 +5,19 @@ import {
RoundedWrapper,
} from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading';
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import * as Types from '@vegaprotocol/types';
import type { ReactNode } from 'react';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
export interface ProtocolUpgradeProposalDetailInfoProps {
proposal: ProtocolUpgradeProposalFieldsFragment;
lastBlockHeight?: string;
time?: string | ReactNode;
}
export const ProtocolUpgradeProposalDetailInfo = ({
proposal,
lastBlockHeight,
time,
}: ProtocolUpgradeProposalDetailInfoProps) => {
const { t } = useTranslation();
const shouldShowUpgradeTime =
![
Types.ProtocolUpgradeProposalStatus
.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED,
Types.ProtocolUpgradeProposalStatus
.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED,
].includes(proposal.status) && time;
const pending = Number(proposal.upgradeBlockHeight) > Number(lastBlockHeight);
return (
<div className="mb-10">
<SubHeading title={t('proposal')} />
@@ -43,26 +29,13 @@ export const ProtocolUpgradeProposalDetailInfo = ({
<span data-testid="protocol-upgrade-block-height">
{proposal.upgradeBlockHeight}{' '}
{lastBlockHeight && (
<span className="text-vega-light-300 dark:text-vega-dark-300">
<>
({t('currently')} {lastBlockHeight})
</span>
</>
)}
</span>
</KeyValueTableRow>
{shouldShowUpgradeTime && (
<KeyValueTableRow>
<span
data-testid="protocol-upgrade-time-label"
className="uppercase"
>
{pending ? t('Estimated time to upgrade') : t('Upgraded at')}
</span>
<span data-testid="protocol-upgrade-time">{time}</span>
</KeyValueTableRow>
)}
<KeyValueTableRow>
<span className="uppercase">{t('state')}</span>
@@ -10,7 +10,10 @@ import {
} from '@vegaprotocol/ui-toolkit';
import { addDecimal, toBigNum } from '@vegaprotocol/utils';
import { ProposalState, VoteValue } from '@vegaprotocol/types';
import { useAppState } from '../../../../contexts/app-state/app-state-context';
import {
AppStateActionType,
useAppState,
} from '../../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../../lib/bignumber';
import { DATE_FORMAT_LONG } from '../../../../lib/date-formats';
import { VoteState } from './use-user-vote';
@@ -70,6 +73,7 @@ export const VoteButtons = ({
dialog: Dialog,
}: VoteButtonsProps) => {
const { t } = useTranslation();
const { appDispatch } = useAppState();
const { pubKey } = useVegaWallet();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
@@ -94,6 +98,10 @@ export const VoteButtons = ({
<div data-testid="connect-wallet">
<ButtonLink
onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog();
}}
>
@@ -134,6 +142,7 @@ export const VoteButtons = ({
minVoterBalance,
spamProtectionMinTokens,
t,
appDispatch,
openVegaWalletDialog,
]);
@@ -14,6 +14,7 @@ const mockAppState: AppState = {
totalAssociated: new BigNumber('50063005'),
decimals: 18,
totalSupply: mockTotalSupply,
vegaWalletOverlay: false,
vegaWalletManageOverlay: false,
transactionOverlay: false,
bannerMessage: '',
File diff suppressed because one or more lines are too long
@@ -3,12 +3,12 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ProposalFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } };
export type ProposalFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } };
export type ProposalsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
export const ProposalFieldsFragmentDoc = gql`
fragment ProposalFields on Proposal {
@@ -1,23 +1,14 @@
import { useEffect, useMemo } from 'react';
import { useParams } from 'react-router-dom';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import {
convertToCountdownString,
formatDateWithLocalTimezone,
removePaginationWrapper,
stripFullStops,
} from '@vegaprotocol/utils';
import { removePaginationWrapper, stripFullStops } from '@vegaprotocol/utils';
import * as Schema from '@vegaprotocol/types';
import { ProtocolUpgradeProposal } from './protocol-upgrade-proposal';
import { ProposalNotFound } from '../components/proposal-not-found';
import { useNodesQuery } from '../../staking/home/__generated__/Nodes';
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
import {
useProtocolUpgradeProposalsQuery,
useTimeToUpgrade,
} from '@vegaprotocol/proposals';
import { useBlockInfo } from '@vegaprotocol/tendermint';
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
export const ProtocolUpgradeProposalContainer = () => {
const params = useParams<{ proposalReleaseTag: string }>();
@@ -67,20 +58,6 @@ export const ProtocolUpgradeProposalContainer = () => {
);
}, [nodesData]);
const pending =
Number(protocolUpgradeProposal?.upgradeBlockHeight) >
Number(data?.lastBlockHeight);
const {
state: { data: blockInfo },
} = useBlockInfo(
Number(protocolUpgradeProposal?.upgradeBlockHeight),
!pending
);
const time = useTimeToUpgrade(
Number(protocolUpgradeProposal?.upgradeBlockHeight)
);
return (
<AsyncRenderer
loading={loading || nodesLoading}
@@ -92,19 +69,6 @@ export const ProtocolUpgradeProposalContainer = () => {
proposal={protocolUpgradeProposal}
lastBlockHeight={data?.lastBlockHeight}
consensusValidators={consensusValidators}
time={
pending && time ? (
convertToCountdownString(time, '0:00:00:00')
) : blockInfo?.result ? (
<span title={blockInfo.result.block.header.time}>
{formatDateWithLocalTimezone(
new Date(blockInfo.result.block.header.time)
)}
</span>
) : (
'-'
)
}
/>
) : (
<ProposalNotFound />
@@ -1,11 +1,10 @@
import { useMemo } from 'react';
import type { ReactNode } from 'react';
import { ProtocolUpgradeProposalDetailHeader } from '../components/protocol-upgrade-proposal-detail-header';
import { ProtocolUpdateProposalDetailApprovals } from '../components/protocol-upgrade-proposal-detail-approvals';
import { ProtocolUpgradeProposalDetailInfo } from '../components/protocol-upgrade-proposal-detail-info';
import { getNormalisedVotingPower } from '../../staking/shared';
import type { NodesFragmentFragment } from '../../staking/home/__generated__/Nodes';
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useVegaRelease } from '@vegaprotocol/environment';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
@@ -14,7 +13,6 @@ export interface ProtocolUpgradeProposalProps {
proposal: ProtocolUpgradeProposalFieldsFragment;
consensusValidators: NodesFragmentFragment[] | null;
lastBlockHeight?: string;
time?: string | ReactNode;
}
export const getConsensusApprovals = (
@@ -46,7 +44,6 @@ export const ProtocolUpgradeProposal = ({
proposal,
lastBlockHeight,
consensusValidators,
time,
}: ProtocolUpgradeProposalProps) => {
const { t } = useTranslation();
const releaseInfo = useVegaRelease(proposal.vegaReleaseTag);
@@ -76,7 +73,6 @@ export const ProtocolUpgradeProposal = ({
<ProtocolUpgradeProposalDetailInfo
proposal={proposal}
lastBlockHeight={lastBlockHeight}
time={time}
/>
{consensusValidators && consensusApprovals && (
@@ -2,9 +2,14 @@ import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { Button } from '@vegaprotocol/ui-toolkit';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
import { SubHeading } from '../../components/heading';
export const ConnectToSeeRewards = () => {
const { appDispatch } = useAppState();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
@@ -21,6 +26,10 @@ export const ConnectToSeeRewards = () => {
<Button
data-testid="connect-to-vega-wallet-btn"
onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog();
}}
>
@@ -216,7 +216,7 @@ export const StakingForm = ({
text: t('associateVegaNow'),
action: () => navigate(Routes.ASSOCIATE),
className: 'py-1',
size: 'small',
size: 'sm',
}}
/>
)}
+13 -8
View File
@@ -2,6 +2,7 @@ import { removeDecimal } from '@vegaprotocol/cypress';
import * as Schema from '@vegaprotocol/types';
import {
OrderStatusMapping,
OrderTimeInForceMapping,
OrderTypeMapping,
Side,
} from '@vegaprotocol/types';
@@ -16,6 +17,7 @@ const orderStatus = 'status';
const orderRemaining = 'remaining';
const orderPrice = 'price';
const orderTimeInForce = 'timeInForce';
const orderCreatedAt = 'createdAt';
const orderUpdatedAt = 'updatedAt';
const assetSelectField = 'select[name="asset"]';
const amountField = 'input[name="amount"]';
@@ -54,7 +56,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
it('can deposit', function () {
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
// 1001-DEPO-001
// 1001-DEPO-002
@@ -117,7 +119,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
it('can key to key transfers', function () {
// 1003-TRAN-023
// 1003-TRAN-006
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId(collateralTab).click();
cy.getByTestId('open-transfer').click();
@@ -260,7 +262,10 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
OrderStatusMapping.STATUS_ACTIVE
);
cy.get(`[col-id='${orderRemaining}']`).should('contain.text', '0.00');
cy.get(`[col-id='${orderRemaining}']`).should(
'contain.text',
`0.00/${order.size}`
);
cy.get(`[col-id='${orderPrice}']`).then(($price) => {
expect(parseFloat($price.text())).to.equal(parseFloat(order.price));
@@ -268,10 +273,10 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
cy.get(`[col-id='${orderTimeInForce}']`).should(
'contain.text',
'GTC'
OrderTimeInForceMapping[order.timeInForce]
);
checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderUpdatedAt);
checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderCreatedAt);
});
});
});
@@ -344,7 +349,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
const ethWalletAddress = Cypress.env('ETHEREUM_WALLET_ADDRESS');
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId(toastCloseBtn, txTimeout).click();
cy.getByTestId('Withdrawals').click();
cy.getByTestId('withdraw-dialog-button').click();
@@ -431,7 +436,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
// 1001-DEPO-006
// 1001-DEPO-007
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId(toastCloseBtn, txTimeout).click();
cy.getByTestId(depositsTab).click();
cy.getByTestId('deposit-button').click();
@@ -453,7 +458,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
// 1002-WITH-007
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]', txTimeout).should('exist');
cy.get('main[data-testid="/portfolio"]', txTimeout).should('exist');
cy.getByTestId(toastCloseBtn, txTimeout).click();
cy.getByTestId(depositsTab).click();
cy.getByTestId('deposit-button').click();
@@ -351,6 +351,12 @@ describe('Closed markets', { tags: '@smoke' }, () => {
)
);
// 6001-MARK-017
cy.get(rowSelector)
.first()
.find('[col-id="realisedPNL"]')
.should('have.text', '-');
// 6001-MARK-018
cy.get(rowSelector)
.first()
@@ -363,10 +369,6 @@ describe('Closed markets', { tags: '@smoke' }, () => {
.first()
.find('button svg')
.should('exist');
cy.get(rowSelector)
.find('[col-id="successorMarketID"]')
.first()
.should('have.text', ' - ');
});
// test market list for market in terminated state
@@ -17,7 +17,7 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
cy.mockTradingPage();
cy.setVegaWallet();
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId('Deposits').click();
cy.getByTestId('deposit-button').click();
connectEthereumWallet('MetaMask');
+1 -1
View File
@@ -106,7 +106,7 @@ describe('home', { tags: '@regression' }, () => {
cy.visit('/');
cy.wait('@Markets');
cy.get('[data-testid^="pathname-/markets/"]');
cy.get('main[data-testid^="/markets/"]');
// the choose market overlay is no longer showing
cy.contains('Loading...').should('not.exist');
@@ -68,15 +68,14 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
validateMarketDataRow(0, 'Name', 'BTCUSD Monthly (30 Jun 2022)');
validateMarketDataRow(1, 'Market ID', 'market-0');
validateMarketDataRow(2, 'Parent Market ID', 'market-1');
validateMarketDataRow(
3,
2,
'Trading Mode',
MarketTradingModeMapping.TRADING_MODE_CONTINUOUS
);
validateMarketDataRow(4, 'Market Decimal Places', '5');
validateMarketDataRow(5, 'Position Decimal Places', '0');
validateMarketDataRow(6, 'Settlement Asset Decimal Places', '5');
validateMarketDataRow(3, 'Market Decimal Places', '5');
validateMarketDataRow(4, 'Position Decimal Places', '0');
validateMarketDataRow(5, 'Settlement Asset Decimal Places', '5');
});
it('instrument displayed', () => {
@@ -37,23 +37,27 @@ describe('markets selector', { tags: '@smoke' }, () => {
const data = [
{
code: 'SOLUSD',
markPrice: '84.41',
vol: '0.00',
markPrice: 'XYZalpha84.41',
change: '',
vol: '24h vol0.00',
},
{
code: 'ETHBTC.QM21',
markPrice: '46,126.90058',
vol: '0.00',
markPrice: 'tBTC46,126.90058',
change: '',
vol: '24h vol0.00',
},
{
code: 'BTCUSD.MF21',
markPrice: '46,126.90058',
vol: '0.00',
markPrice: 'tDAI46,126.90058',
change: '',
vol: '24h vol0.00',
},
{
code: 'AAPL.MF21',
markPrice: '46,126.90058',
vol: '0.00',
markPrice: 'tUSDC46,126.90058',
change: '',
vol: '24h vol0.00',
},
];
cy.getByTestId('header-title').should('be.visible').click();
@@ -65,12 +69,16 @@ describe('markets selector', { tags: '@smoke' }, () => {
// 6001-MARK-022
expect(item.find('h3').text()).equals(market.code);
expect(
item.find('[data-testid="market-selector-volume"]').text()
item.find('[data-testid="market-selector-data-row"]').eq(0).text()
).contains(market.vol);
// 6001-MARK-024
expect(
item.find('[data-testid="market-selector-price"]').text()
item.find('[data-testid="market-selector-data-row"]').eq(1).text()
).contains(market.markPrice);
// 6001-MARK-023
expect(item.find('[data-testid="market-item-change"]').text()).equals(
market.change
);
// 6001-MARK-025
expect(item.find('[data-testid="sparkline-svg"]')).to.not.exist;
});
@@ -9,7 +9,6 @@ const bidCumulative = 'cumulative-vol-9889001';
const midPrice = 'middle-mark-price-4612690000';
const priceResolution = 'resolution';
const dealTicketPrice = 'order-price';
const dealTicketSize = 'order-size';
const resPrice = 'price-990';
describe('order book', { tags: '@smoke' }, () => {
@@ -75,18 +74,6 @@ describe('order book', { tags: '@smoke' }, () => {
cy.getByTestId(dealTicketPrice).should('have.value', '98.94185');
});
it('copy size to deal ticket form', () => {
// 6003-ORDB-009
cy.getByTestId(bidCumulative).click();
cy.getByTestId(dealTicketSize).should('have.value', '7');
});
it('copy size to deal ticket form', () => {
// 6003-ORDB-009
cy.getByTestId(bidVolume).click();
cy.getByTestId(dealTicketSize).should('have.value', '1');
});
it('change price resolution', () => {
// 6003-ORDB-008
const resolutions = [
@@ -101,14 +88,13 @@ describe('order book', { tags: '@smoke' }, () => {
'1,000',
'10,000',
];
cy.getByTestId(priceResolution).click();
cy.get('[role="menu"]')
.find('[role="menuitem"]')
cy.getByTestId(priceResolution)
.find('option')
.each(($el, index) => {
expect($el.text()).to.equal(resolutions[index]);
});
cy.get('[role="menuitem"]').eq(4).click();
cy.getByTestId(priceResolution).select('0.0');
cy.getByTestId(resPrice).should('have.text', '99.0');
cy.getByTestId(askPrice).should('not.exist');
cy.getByTestId(bidPrice).should('not.exist');
@@ -14,12 +14,6 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.get('[data-testid="deal-ticket-form"]').then(($form) => {
if (!$form.length) {
cy.getByTestId('Order').click();
}
});
});
beforeEach(() => {
@@ -59,12 +59,6 @@ describe(
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.get('[data-testid="deal-ticket-form"]').then(($form) => {
if (!$form.length) {
cy.getByTestId('Order').click();
}
});
});
it('should display info and button for deposit', () => {
@@ -37,7 +37,7 @@ describe('fills', { tags: '@regression' }, () => {
it('renders fills on portfolio page', () => {
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId('Fills').click();
validateFillsDisplayed();
});
@@ -50,7 +50,7 @@ describe('Portfolio page', { tags: '@smoke' }, () => {
cy.get('fieldset.ag-simple-filter-body-wrapper')
.should('be.visible')
.within((fields) => {
cy.wrap(fields).find('label').should('have.length', 18);
cy.wrap(fields).find('label').should('have.length', 16);
});
cy.getByTestId('"Ledger entries"').click();
cy.get('fieldset.ag-simple-filter-body-wrapper').should('not.exist');
@@ -10,7 +10,7 @@ describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
cy.mockSubscription();
cy.setVegaWallet();
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId('Withdrawals').click();
});
@@ -17,7 +17,7 @@ describe(
cy.visit('/#/portfolio');
cy.mockTradingPage();
cy.mockSubscription();
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
});
it('can connect', () => {
@@ -63,7 +63,7 @@ describe(
cy.contains('Hosted Fairground wallet');
cy.getByTestId('connectors-list')
.find('[data-testid="connector-rest"]')
.find('[data-testid="connector-hosted"]')
.click();
cy.getByTestId(form).find('#wallet').click().type('user');
cy.getByTestId(form).find('#passphrase').click().type('pass');
@@ -89,7 +89,7 @@ describe(
);
cy.getByTestId(connectVegaBtn).click();
cy.getByTestId('connectors-list')
.find('[data-testid="connector-rest"]')
.find('[data-testid="connector-hosted"]')
.click();
cy.getByTestId(form).find('#wallet').click().type('invalid name');
cy.getByTestId(form).find('#passphrase').click().type('invalid password');
@@ -100,7 +100,7 @@ describe(
it('doesnt connect with empty fields', () => {
cy.getByTestId(connectVegaBtn).click();
cy.getByTestId('connectors-list')
.find('[data-testid="connector-rest"]')
.find('[data-testid="connector-hosted"]')
.click();
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
@@ -122,7 +122,7 @@ describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
cy.visit('/#/portfolio');
cy.mockTradingPage();
cy.mockSubscription();
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
});
it('can connect', () => {
@@ -1,5 +1,5 @@
import { selectAsset } from '../support/helpers';
// #region consts
const amountField = 'input[name="amount"]';
const amountShortName = 'input[name="amount"] + div + span.text-xs';
const assetSelection = 'select-asset';
@@ -26,6 +26,7 @@ const ASSET_SEPOLIA_TBTC = 2;
const collateralTab = 'Collateral';
const toastCloseBtn = 'toast-close';
const toastContent = 'toast-content';
// #endregion
describe('transfer fees', { tags: '@regression', testIsolation: true }, () => {
beforeEach(() => {
@@ -36,17 +37,17 @@ describe('transfer fees', { tags: '@regression', testIsolation: true }, () => {
cy.visit('/');
cy.wait('@Assets');
cy.wait('@Accounts');
cy.mockVegaWalletTransaction();
// Only click if not already active otherwise sidebar will close
cy.get('[data-testid="sidebar-content"]').then(($sidebarContent) => {
if ($sidebarContent.find('h2').text() !== 'Transfer') {
cy.get('[data-testid="sidebar"] [data-testid="Transfer"]').click();
}
});
cy.wait('@Assets');
cy.wait('@Accounts');
cy.mockVegaWalletTransaction();
});
it('transfer fees tooltips', () => {
+1 -1
View File
@@ -14,4 +14,4 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
# TAG name of the current app version - TODO: bump to the latest upon release
NX_APP_VERSION=v0.20.21-core-0.71.6
NX_APP_VERSION=v0.20.19-core-0.71.6
+2 -2
View File
@@ -3,9 +3,9 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
NX_VEGA_ENV=MAINNET_MIRROR
NX_VEGA_ENV=MAINNET-MIRROR
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks
NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\",\"MAINNET_MIRROR\":\"https://trading.mainnet-mirror.vega.rocks\"}
NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\",\"MAINNET-MIRROR\":\"https://trading.mainnet-mirror.vega.rocks\"}
NX_VEGA_TOKEN_URL=https://governance.mainnet-mirror.vega.rocks
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
@@ -3,7 +3,7 @@ import { t } from '@vegaprotocol/i18n';
export const Disclaimer = () => {
return (
<div className="py-16 px-8 flex w-full justify-center">
<div className="lg:min-w-[700px] min-w-[300px] max-w-[700px]">
<div className="lg:max-w-[800px]">
<h1 className="text-4xl xl:text-5xl uppercase font-alpha calt">
{t('Disclaimer')}
</h1>
@@ -0,0 +1 @@
export * from './layout';
@@ -0,0 +1,60 @@
import { Outlet, useLocation } from 'react-router-dom';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { Sidebar, SidebarContent, useSidebar } from '../../components/sidebar';
import classNames from 'classnames';
import { Navbar } from '../../components/navbar';
import { ViewingBanner } from '../../components/viewing-banner';
import { AnnouncementBanner, UpgradeBanner } from '../../components/banner';
import {
ProtocolUpgradeCountdownMode,
ProtocolUpgradeProposalNotification,
} from '@vegaprotocol/proposals';
export const Layout = () => {
const location = useLocation();
const { VEGA_ENV } = useEnvironment();
const { view } = useSidebar();
const sidebarOpen = view !== null;
const gridClasses = classNames(
'h-full relative z-0 grid',
'grid-rows-[repeat(4,min-content),minmax(0,1fr)]',
{
'grid-cols-[45px_350px_1fr]': sidebarOpen,
'grid-cols-[45px_1fr]': !sidebarOpen,
}
);
const mainClasses = classNames('row-start-4 row-span-full');
return (
<div className={gridClasses}>
<div className="col-span-full row-start-1 bg-vega-pink">
<AnnouncementBanner />
</div>
<div className="row-span-full row-start-2 bg-vega-light-100 dark:bg-vega-dark-100 border-r border-default">
<Sidebar />
</div>
{sidebarOpen && (
<div className="row-span-full row-start-4 col-start-2 p-4">
<SidebarContent />
</div>
)}
<div className="col-span-full col-start-2 row-start-2 bg-vega-yellow">
<Navbar theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
</div>
<div
className="col-span-full col-start-2 row-start-3 bg-vega-pink-350"
data-testid="banners"
>
<ProtocolUpgradeProposalNotification
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
<ViewingBanner />
<UpgradeBanner showVersionChange={true} />
</div>
<main className={mainClasses} data-testid={location.pathname}>
<Outlet />
</main>
</div>
);
};
+137 -26
View File
@@ -1,10 +1,29 @@
import { matchFilter, lpAggregatedDataProvider } from '@vegaprotocol/liquidity';
import {
matchFilter,
lpAggregatedDataProvider,
useCheckLiquidityStatus,
} from '@vegaprotocol/liquidity';
import { tooltipMapping } from '@vegaprotocol/markets';
import {
addDecimalsFormatNumber,
formatNumberPercentage,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { Tab, Tabs } from '@vegaprotocol/ui-toolkit';
import { Tab, Tabs, Indicator, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useEffect, useState } from 'react';
import { memo, useEffect, useState } from 'react';
import { Header, HeaderStat, HeaderTitle } from '../../components/header';
import { useParams } from 'react-router-dom';
import { useMarket, useStaticMarketData } from '@vegaprotocol/markets';
import { DocsLinks } from '@vegaprotocol/environment';
import { LiquidityContainer } from '../../components/liquidity-container';
const enum LiquidityTabs {
@@ -19,6 +38,92 @@ export const Liquidity = () => {
return <LiquidityViewContainer marketId={marketId} />;
};
const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
const { data: market } = useMarket(marketId);
const { data: marketData } = useStaticMarketData(marketId);
const targetStake = marketData?.targetStake;
const suppliedStake = marketData?.suppliedStake;
const assetDecimalPlaces =
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
const symbol =
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
const { params } = useNetworkParams([
NetworkParams.market_liquidity_stakeToCcyVolume,
NetworkParams.market_liquidity_targetstake_triggering_ratio,
]);
const triggeringRatio =
params.market_liquidity_targetstake_triggering_ratio || '1';
const { percentage, status } = useCheckLiquidityStatus({
suppliedStake: suppliedStake || 0,
targetStake: targetStake || 0,
triggeringRatio,
});
return (
<Header
title={
market?.tradableInstrument.instrument.code &&
marketId && (
<HeaderTitle>
{market.tradableInstrument.instrument.code &&
t(
'%s liquidity provision',
market.tradableInstrument.instrument.code
)}
</HeaderTitle>
)
}
>
<HeaderStat
heading={t('Target stake')}
description={tooltipMapping['targetStake']}
testId="target-stake"
>
<div>
{targetStake
? `${addDecimalsFormatNumber(
targetStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat
heading={t('Supplied stake')}
description={tooltipMapping['suppliedStake']}
testId="supplied-stake"
>
<div>
{suppliedStake
? `${addDecimalsFormatNumber(
suppliedStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat heading={t('Liquidity supplied')} testId="liquidity-supplied">
<Indicator variant={status} /> {formatNumberPercentage(percentage, 2)}
</HeaderStat>
<HeaderStat heading={t('Market ID')} testId="liquidity-market-id">
<div className="break-word">{marketId}</div>
</HeaderStat>
<HeaderStat heading={t('Learn more')} testId="liquidity-learn-more">
{DocsLinks ? (
<ExternalLink href={DocsLinks.LIQUIDITY}>
{t('Providing liquidity')}
</ExternalLink>
) : (
(null as React.ReactNode)
)}
</HeaderStat>
</Header>
);
});
LiquidityViewHeader.displayName = 'LiquidityViewHeader';
export const LiquidityViewContainer = ({
marketId,
}: {
@@ -49,29 +154,35 @@ export const LiquidityViewContainer = ({
}, [data, pubKey]);
return (
<div className="h-full p-1.5">
<div className="h-full border border-default">
<Tabs value={tab || LiquidityTabs.Active} onValueChange={setTab}>
<Tab
id={LiquidityTabs.MyLiquidityProvision}
name={t('My liquidity provision')}
hidden={!pubKey}
>
<LiquidityContainer
marketId={marketId}
filter={{ partyId: pubKey || undefined }}
/>
</Tab>
<Tab id={LiquidityTabs.Active} name={t('Active')}>
<LiquidityContainer marketId={marketId} filter={{ active: true }} />
</Tab>
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
<LiquidityContainer
marketId={marketId}
filter={{ active: false }}
/>
</Tab>
</Tabs>
<div className="h-full grid grid-rows-[min-content_1fr]">
<LiquidityViewHeader marketId={marketId} />
<div className="p-1">
<div className="h-full border border-default">
<Tabs value={tab || LiquidityTabs.Active} onValueChange={setTab}>
<Tab
id={LiquidityTabs.MyLiquidityProvision}
name={t('My liquidity provision')}
hidden={!pubKey}
>
<LiquidityContainer
marketId={marketId}
filter={{ partyId: pubKey || undefined }}
/>
</Tab>
<Tab id={LiquidityTabs.Active} name={t('Active')}>
<LiquidityContainer
marketId={marketId}
filter={{ active: true }}
/>
</Tab>
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
<LiquidityContainer
marketId={marketId}
filter={{ active: false }}
/>
</Tab>
</Tabs>
</div>
</div>
</div>
);
@@ -19,6 +19,7 @@ describe('AssetDropdown', () => {
checkedAssets={[]}
assets={assets}
onSelect={mockOnSelect}
onReset={jest.fn()}
/>
);
await userEvent.click(screen.getByRole('button'));
@@ -38,6 +39,7 @@ describe('AssetDropdown', () => {
checkedAssets={[assets[0].id]}
assets={assets}
onSelect={mockOnSelect}
onReset={jest.fn()}
/>
);
await userEvent.click(screen.getByRole('button'));
@@ -46,9 +48,35 @@ describe('AssetDropdown', () => {
expect(mockOnSelect).toHaveBeenCalledWith(assets[0].id, false);
});
it('can be reset clearing all assets', async () => {
const mockOnSelect = jest.fn();
const mockOnReset = jest.fn();
render(
<AssetDropdown
checkedAssets={assets.map((a) => a.id)}
assets={assets}
onSelect={mockOnSelect}
onReset={mockOnReset}
/>
);
await userEvent.click(screen.getByRole('button'));
const items = screen.getAllByRole('menuitemcheckbox');
// all should be checked
items.forEach((item) => {
expect(item).toBeChecked();
});
await userEvent.click(screen.getByText('Reset'));
expect(mockOnReset).toHaveBeenCalled();
});
it('doesnt render if no assets provided', async () => {
const { container } = render(
<AssetDropdown checkedAssets={[]} assets={[]} onSelect={jest.fn()} />
<AssetDropdown
checkedAssets={[]}
assets={[]}
onSelect={jest.fn()}
onReset={jest.fn()}
/>
);
expect(container).toBeEmptyDOMElement();
});
@@ -3,22 +3,22 @@ import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuItemIndicator,
DropdownMenuTrigger,
VegaIcon,
VegaIconNames,
DropdownMenuSeparator,
} from '@vegaprotocol/ui-toolkit';
type Assets = Array<{ id: string; symbol: string }>;
export const AssetDropdown = ({
assets,
checkedAssets,
onSelect,
onReset,
}: {
assets: Assets | undefined;
assets: Array<{ id: string; symbol: string }> | undefined;
checkedAssets: string[];
onSelect: (id: string, checked: boolean) => void;
onReset: () => void;
}) => {
if (!assets?.length) {
return null;
@@ -28,11 +28,13 @@ export const AssetDropdown = ({
<DropdownMenu
trigger={
<DropdownMenuTrigger data-testid="asset-trigger">
<TriggerText assets={assets} checkedAssets={checkedAssets} />
<span className="px-1">$</span>
</DropdownMenuTrigger>
}
>
<DropdownMenuContent>
<DropdownMenuItem onClick={onReset}>{t('Reset')}</DropdownMenuItem>
<DropdownMenuSeparator />
{assets?.map((a) => {
return (
<DropdownMenuCheckboxItem
@@ -54,27 +56,3 @@ export const AssetDropdown = ({
</DropdownMenu>
);
};
const TriggerText = ({
assets,
checkedAssets,
}: {
assets: Assets;
checkedAssets: string[];
}) => {
let text = t('Assets');
if (checkedAssets.length === 1) {
const assetId = checkedAssets[0];
const asset = assets.find((a) => a.id === assetId);
text = asset ? asset.symbol : t('Asset (1)');
} else if (checkedAssets.length > 1) {
text = t(`${checkedAssets.length} Assets`);
}
return (
<span className="flex justify-between items-center">
{text} <VegaIcon name={VegaIconNames.CHEVRON_DOWN} />
</span>
);
};
@@ -169,7 +169,7 @@ describe('MarketSelectorItem', () => {
// link renders and is styled
expect(link).toHaveAttribute('href', '/markets/' + market.id);
expect(link).toHaveClass('bg-vega-clight-600');
expect(link.parentNode).toHaveClass('bg-vega-light-100');
expect(screen.getByTitle('24h vol')).toHaveTextContent('0.00');
expect(screen.getByTitle(symbol)).toHaveTextContent('-');
@@ -1,4 +1,4 @@
import type { CSSProperties } from 'react';
import type { CSSProperties, ReactNode } from 'react';
import { Link } from 'react-router-dom';
import classNames from 'classnames';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
@@ -22,17 +22,12 @@ export const MarketSelectorItem = ({
style: CSSProperties;
currentMarketId?: string;
}) => {
const wrapperClasses = classNames('py-1 px-4', {
'bg-vega-light-100 dark:bg-vega-dark-100': market.id === currentMarketId,
});
return (
<div style={style} role="row">
<Link
to={`/markets/${market.id}`}
className={classNames('h-full flex items-center gap-2 px-4', {
'hover:bg-vega-clight-700 dark:hover:bg-vega-cdark-700':
market.id !== currentMarketId,
'bg-vega-clight-600 dark:bg-vega-cdark-600':
market.id === currentMarketId,
})}
>
<div style={style} className={wrapperClasses}>
<Link to={`/markets/${market.id}`} className="block">
<MarketData market={market} />
</Link>
</div>
@@ -78,8 +73,8 @@ const MarketData = ({ market }: { market: MarketMaybeWithDataAndCandles }) => {
: '0.00';
return (
<>
<div className="w-2/5" role="gridcell">
<div className="flex gap-2">
<div>
<h3 className="text-ellipsis whitespace-nowrap overflow-hidden">
{market.tradableInstrument.instrument.code}
</h3>
@@ -89,31 +84,44 @@ const MarketData = ({ market }: { market: MarketMaybeWithDataAndCandles }) => {
</p>
)}
</div>
<div
className="w-1/5 text-sm whitespace-nowrap text-ellipsis overflow-hidden"
title={instrument.product.settlementAsset.symbol}
data-testid="market-selector-price"
role="gridcell"
>
{price} {instrument.product.settlementAsset.symbol}
<div className="flex-1">
<DataRow value={volume} label={t('24h vol')} />
<DataRow
value={price}
label={instrument.product.settlementAsset.symbol}
/>
</div>
<div
className="w-1/5 text-sm text-right whitespace-nowrap text-ellipsis overflow-hidden"
title={t('24h vol')}
data-testid="market-selector-volume"
role="gridcell"
>
{volume}
</div>
<div className="w-1/5 flex justify-end" role="gridcell">
<div className="hidden lg:block w-[70px]">
{oneDayCandles && (
<Sparkline
width={64}
width={70}
height={15}
data={oneDayCandles.map((c) => Number(c.close))}
/>
)}
</div>
</>
</div>
);
};
const DataRow = ({
value,
label,
}: {
value: string | ReactNode;
label: string;
}) => {
return (
<div
className="text-ellipsis whitespace-nowrap overflow-hidden leading-tight text-right"
data-testid="market-selector-data-row"
>
<span className="text-xs text-vega-light-300 dark:text-vega-light-300">
{label}
</span>
<span title={label} className="text-sm ml-1">
{value}
</span>
</div>
);
};
@@ -240,7 +240,7 @@ describe('MarketSelector', () => {
await userEvent.click(screen.getByTestId('sort-trigger'));
const options = screen.getAllByTestId(/sort-item/);
expect(options.map((o) => o.textContent?.trim())).toEqual(
expect(options.map((o) => o.textContent)).toEqual(
Object.entries(Sort)
.filter(([key]) => key !== Sort.None)
.map(([key]) => SortTypeMapping[key as SortType])
@@ -8,7 +8,7 @@ import {
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import type { CSSProperties } from 'react';
import { useCallback, useState, useMemo, useRef } from 'react';
import { useCallback, useState, useMemo } from 'react';
import { FixedSizeList } from 'react-window';
import { useMarketSelectorList } from './use-market-selector-list';
import type { ProductType } from './product-selector';
@@ -17,7 +17,6 @@ import { AssetDropdown } from './asset-dropdown';
import type { SortType } from './sort-dropdown';
import { Sort, SortDropdown } from './sort-dropdown';
import { MarketSelectorItem } from './market-selector-item';
import classNames from 'classnames';
export type Filter = {
searchTerm: string;
@@ -48,14 +47,14 @@ export const MarketSelector = ({
return (
<div data-testid="market-selector">
<div className="pt-2 px-2 mb-2 w-[320px] lg:w-[584px]">
<div className="pt-2 px-4 mb-2 w-[320px] lg:w-[400px]">
<ProductSelector
product={filter.product}
onSelect={(product) => {
setFilter((curr) => ({ ...curr, product }));
}}
/>
<div className="text-sm grid grid-cols-[2fr_1fr_1fr] gap-1 ">
<div className="text-sm flex gap-1 items-stretch">
<div className="flex-1">
<Input
onChange={(e) =>
@@ -66,7 +65,20 @@ export const MarketSelector = ({
placeholder={t('Search')}
data-testid="search-term"
className="w-full"
prependElement={<VegaIcon name={VegaIconNames.SEARCH} />}
appendElement={
filter.searchTerm.length ? (
<button
onClick={() =>
setFilter((curr) => ({ ...curr, searchTerm: '' }))
}
className="text-vega-light-200 dark:text-vega-dark-200"
>
<VegaIcon name={VegaIconNames.CROSS} />
</button>
) : (
<span />
)
}
/>
</div>
<AssetDropdown
@@ -96,6 +108,7 @@ export const MarketSelector = ({
return curr;
});
}}
onReset={() => setFilter((curr) => ({ ...curr, assets: [] }))}
/>
<SortDropdown
currentSort={filter.sort}
@@ -110,6 +123,7 @@ export const MarketSelector = ({
};
});
}}
onReset={() => setFilter((curr) => ({ ...curr, sort: Sort.None }))}
/>
</div>
</div>
@@ -150,49 +164,19 @@ const MarketList = ({
onSelect?: (marketId: string) => void;
noItems: string;
}) => {
const itemSize = 45;
const listRef = useRef<HTMLDivElement | null>(null);
const rect = listRef.current?.getBoundingClientRect();
// allow virtualized list to grow until it runs out of space
const height = rect
? Math.min(data.length * itemSize, window.innerHeight - rect.y)
: 400;
if (error) {
return <div>{error.message}</div>;
}
return (
<TinyScroll>
<div
className={classNames(
'flex gap-2',
'bg-vega-clight-700 dark:bg-vega-cdark-700',
'p-2 mx-2 border-b border-default text-xs text-secondary'
)}
>
<div className="w-2/5" role="columnheader">
{t('Name')}
</div>
<div className="w-1/5" role="columnheader">
{t('Price')}
</div>
<div className="w-1/5 text-right" role="columnheader">
{t('24h volume')}
</div>
<div className="w-1/5" role="columnheader" />
</div>
<div ref={listRef}>
<List
data={data}
loading={loading}
height={height}
itemSize={itemSize}
currentMarketId={currentMarketId}
onSelect={onSelect}
noItems={noItems}
/>
</div>
<List
data={data}
loading={loading}
height={400}
currentMarketId={currentMarketId}
onSelect={onSelect}
noItems={noItems}
/>
</TinyScroll>
);
};
@@ -223,14 +207,12 @@ const List = ({
data,
loading,
height,
itemSize,
onSelect,
noItems,
currentMarketId,
}: ListItemData & {
loading: boolean;
height: number;
itemSize: number;
noItems: string;
}) => {
const itemKey = useCallback(
@@ -253,7 +235,11 @@ const List = ({
if (!data.length) {
return (
<div style={{ height }} data-testid="no-items">
<div className="mx-4 my-2 text-sm">{noItems}</div>
<div className="mb-2 px-4">
<div className="text-sm bg-vega-light-100 dark:bg-vega-dark-100 rounded-lg px-4 py-2">
{noItems}
</div>
</div>
</div>
);
}
@@ -263,7 +249,7 @@ const List = ({
className="vega-scrollbar"
itemCount={data.length}
itemData={itemData}
itemSize={itemSize}
itemSize={50}
itemKey={itemKey}
width="100%"
height={height}
+10 -3
View File
@@ -2,9 +2,12 @@ import React, { useEffect, useMemo } from 'react';
import { addDecimalsFormatNumber, titlefy } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useThrottledDataProvider } from '@vegaprotocol/data-provider';
import {
useDataProvider,
useThrottledDataProvider,
} from '@vegaprotocol/data-provider';
import { AsyncRenderer, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
import { marketDataProvider, useMarket } from '@vegaprotocol/markets';
import { marketProvider, marketDataProvider } from '@vegaprotocol/markets';
import { useGlobalStore, usePageTitleStore } from '../../stores';
import { TradeGrid } from './trade-grid';
import { TradePanels } from './trade-panels';
@@ -67,7 +70,11 @@ export const MarketPage = () => {
const onSelect = useMarketClickHandler();
const { data, error, loading } = useMarket(marketId);
const { data, error, loading } = useDataProvider({
dataProvider: marketProvider,
variables: { marketId: marketId || '' },
skip: !marketId,
});
useEffect(() => {
if (data?.id && data.id !== lastMarketId) {
@@ -1,8 +1,4 @@
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import { Routes } from '../../pages/client-router';
import { t } from '@vegaprotocol/i18n';
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
// Make sure these match the available __typename properties on product
export const Product = {
@@ -29,12 +25,11 @@ export const ProductSelector = ({
onSelect: (product: ProductType) => void;
}) => {
return (
<div className="flex mb-2">
<div className="flex gap-3 mb-3">
{Object.keys(Product).map((t) => {
const classes = classNames('px-3 py-1.5 rounded', {
'bg-vega-clight-500 dark:bg-vega-cdark-500 text-default':
t === product,
'text-secondary': t !== product,
const classes = classNames('py-1 border-b-2', {
'border-vega-yellow text-default': t === product,
'border-transparent text-muted': t !== product,
});
return (
<button
@@ -49,10 +44,6 @@ export const ProductSelector = ({
</button>
);
})}
<Link to={Routes.MARKETS} className="flex items-center gap-2 ml-auto">
<span className="underline underline-offset-4">{t('All markets')}</span>
<VegaIcon name={VegaIconNames.ARROW_RIGHT} />
</Link>
</div>
);
};
@@ -2,10 +2,12 @@ import { t } from '@vegaprotocol/i18n';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuItemIndicator,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
DropdownMenuSeparator,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
@@ -28,32 +30,20 @@ export const SortTypeMapping: {
[Sort.New]: 'New markets',
};
const SortIconMapping: {
[key in SortType]: VegaIconNames;
} = {
[Sort.None]: null as unknown as VegaIconNames, // not shown in list
[Sort.Gained]: VegaIconNames.TREND_UP,
[Sort.Lost]: VegaIconNames.TREND_DOWN,
[Sort.New]: VegaIconNames.STAR,
};
export const SortDropdown = ({
currentSort,
onSelect,
onReset,
}: {
currentSort: SortType;
onSelect: (sort: SortType) => void;
onReset: () => void;
}) => {
return (
<DropdownMenu
trigger={
<DropdownMenuTrigger data-testid="sort-trigger">
<span className="flex justify-between items-center">
{currentSort === SortTypeMapping.None
? t('Sort')
: SortTypeMapping[currentSort]}{' '}
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} />
</span>
<VegaIcon name={VegaIconNames.TREND_UP} />
</DropdownMenuTrigger>
}
>
@@ -62,6 +52,8 @@ export const SortDropdown = ({
value={currentSort}
onValueChange={(value) => onSelect(value as SortType)}
>
<DropdownMenuItem onClick={onReset}>{t('Reset')}</DropdownMenuItem>
<DropdownMenuSeparator />
{Object.keys(Sort)
.filter((s) => s !== Sort.None)
.map((key) => {
@@ -72,10 +64,7 @@ export const SortDropdown = ({
value={key}
data-testid={`sort-item-${key}`}
>
<span className="flex gap-2">
<VegaIcon name={SortIconMapping[key as SortType]} />{' '}
{SortTypeMapping[key as SortType]}
</span>
{SortTypeMapping[key as SortType]}
<DropdownMenuItemIndicator />
</DropdownMenuRadioItem>
);
@@ -6,18 +6,27 @@ import AutoSizer from 'react-virtualized-auto-sizer';
import type { PinnedAsset } from '@vegaprotocol/accounts';
import { t } from '@vegaprotocol/i18n';
import { OracleBanner } from '@vegaprotocol/markets';
import type { Market } from '@vegaprotocol/markets';
import type { Market, MarketFieldsFragment } from '@vegaprotocol/markets';
import { Filter } from '@vegaprotocol/orders';
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
import {
Popover,
Tab,
LocalStoragePersistTabs as Tabs,
Tooltip,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { Header, HeaderTitle } from '../../components/header';
import {
ResizableGrid,
ResizableGridPanel,
usePaneLayout,
} from '../../components/resizable-grid';
import { TradingViews } from './trade-views';
import { MarketSuccessorBanner } from '../../components/market-banner';
import { MarketSelector } from './market-selector';
import { MarketHeaderStats } from './market-header-stats';
interface TradeGridProps {
market: Market | null;
onSelect: (marketId: string, metaKey?: boolean) => void;
@@ -154,16 +163,31 @@ MainGrid.displayName = 'MainGrid';
export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
const wrapperClasses = classNames(
'h-full grid',
'grid-rows-[min-content_1fr]'
'grid-rows-[min-content_min-content_1fr]'
);
return (
<div className={wrapperClasses}>
<div>
<MarketSuccessorBanner market={market} />
<Header
title={
<Popover
trigger={
<HeaderTitle>
{market?.tradableInstrument.instrument.code}
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={14} />
</HeaderTitle>
}
>
<MarketSelector currentMarketId={market?.id} />
</Popover>
}
>
<MarketHeaderStats market={market} />
</Header>
<div className="bg-vega-green">
<OracleBanner marketId={market?.id || ''} />
</div>
<div className="min-h-0 p-0.5">
<div className="min-h-0 py-0.5">
<MainGrid marketId={market?.id || ''} pinnedAsset={pinnedAsset} />
</div>
</div>
@@ -8,11 +8,18 @@ import {
import type { TradingView } from './trade-views';
import { TradingViews } from './trade-views';
import { memo, useState } from 'react';
import { Splash } from '@vegaprotocol/ui-toolkit';
import {
Popover,
Splash,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { NO_MARKET } from './constants';
import AutoSizer from 'react-virtualized-auto-sizer';
import classNames from 'classnames';
import { MarketSuccessorBanner } from '../../components/market-banner';
import { Header, HeaderTitle } from '../../components/header';
import { MarketSelector } from './market-selector';
import { MarketHeaderStats } from './market-header-stats';
interface TradePanelsProps {
market: Market | null;
@@ -62,9 +69,24 @@ export const TradePanels = ({
};
return (
<div className="h-full grid grid-rows-[min-content_1fr_min-content]">
<div className="h-full grid grid-rows-[min-content_min-content_1fr_min-content]">
<Header
title={
<Popover
trigger={
<HeaderTitle>
{market?.tradableInstrument.instrument.code}
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} />
</HeaderTitle>
}
>
<MarketSelector currentMarketId={market?.id} />
</Popover>
}
>
<MarketHeaderStats market={market} />
</Header>
<div>
<MarketSuccessorBanner market={market} />
<OracleBanner marketId={market?.id || ''} />
</div>
<div className="h-full">
@@ -76,10 +98,10 @@ export const TradePanels = ({
)}
</AutoSizer>
</div>
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default bg-vega-clight-800 dark:bg-vega-cdark-800">
{Object.keys(TradingViews).map((key) => {
const isActive = view === key;
const className = classNames('p-4 min-w-[100px] capitalize', {
const className = classNames('p-2 px-3 capitalize', {
'bg-vega-clight-500 dark:bg-vega-cdark-500': isActive,
});
return (
@@ -3,9 +3,9 @@ import { Splash } from '@vegaprotocol/ui-toolkit';
import { TradesContainer } from '@vegaprotocol/trades';
import { DepthChartContainer } from '@vegaprotocol/market-depth';
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
import { OrderbookContainer } from '@vegaprotocol/market-depth';
import { Filter } from '@vegaprotocol/orders';
import { NO_MARKET } from './constants';
import { OrderbookContainer } from '../../components/orderbook-container';
import { FillsContainer } from '../../components/fills-container';
import { PositionsContainer } from '../../components/positions-container';
import { AccountsContainer } from '../../components/accounts-container';
@@ -4,12 +4,12 @@ import {
isMarketActive,
useMarketSelectorList,
} from './use-market-selector-list';
import { Product } from '../../components/market-selector/product-selector';
import { Product } from './product-selector';
import { Sort } from './sort-dropdown';
import { createMarketFragment } from '@vegaprotocol/mock';
import { MarketState } from '@vegaprotocol/types';
import { useMarketList } from '@vegaprotocol/markets';
import type { Filter } from '../../components/market-selector';
import type { Filter } from './market-selector';
import { subDays } from 'date-fns';
jest.mock('@vegaprotocol/markets');
@@ -3,7 +3,7 @@ import orderBy from 'lodash/orderBy';
import { MarketState } from '@vegaprotocol/types';
import { calcCandleVolume, useMarketList } from '@vegaprotocol/markets';
import { priceChangePercentage } from '@vegaprotocol/utils';
import type { Filter } from '../../components/market-selector/market-selector';
import type { Filter } from './market-selector';
import { Sort } from './sort-dropdown';
// Used for sort order and filter
@@ -1,7 +1,7 @@
import { act, render, screen, within, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { act, render, screen, within } from '@testing-library/react';
import { Closed } from './closed';
import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types';
import { PositionStatus } from '@vegaprotocol/types';
import { MarketState } from '@vegaprotocol/types';
import { subDays } from 'date-fns';
import type { MockedResponse } from '@apollo/client/testing';
@@ -18,6 +18,11 @@ import {
} from '@vegaprotocol/markets';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import type {
PositionsQuery,
PositionFieldsFragment,
} from '@vegaprotocol/positions';
import { PositionsDocument } from '@vegaprotocol/positions';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import {
createMarketFragment,
@@ -155,6 +160,45 @@ describe('Closed', () => {
},
};
// Create mock position
const createPosition = (): PositionFieldsFragment => {
return {
__typename: 'Position' as const,
realisedPNL: '1000',
unrealisedPNL: '2000',
openVolume: '3000',
averageEntryPrice: '100',
updatedAt: new Date().toISOString(),
positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED,
lossSocializationAmount: '1000',
market: {
__typename: 'Market',
id: marketId,
},
party: {
__typename: 'Party',
id: pubKey,
},
};
};
const position = createPosition();
const positionsMock: MockedResponse<PositionsQuery> = {
request: {
query: PositionsDocument,
variables: {
partyIds: [pubKey],
},
},
result: {
data: {
positions: {
__typename: 'PositionConnection',
edges: [{ __typename: 'PositionEdge', node: position }],
},
},
},
};
beforeAll(() => {
originalNow = Date.now;
Date.now = jest.fn().mockReturnValue(mockNowTimestamp);
@@ -167,17 +211,15 @@ describe('Closed', () => {
it('renders correctly formatted and filtered rows', async () => {
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[marketsMock, marketsDataMock, oracleDataMock]}
<MockedProvider
mocks={[marketsMock, marketsDataMock, positionsMock, oracleDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
);
});
// screen.debug(document, Infinity);
@@ -188,11 +230,11 @@ describe('Closed', () => {
'Description',
'Status',
'Settlement date',
'Successor market',
'Best bid',
'Best offer',
'Mark price',
'Settlement price',
'Realised PNL',
'Settlement asset',
'', // actions row
];
@@ -205,7 +247,6 @@ describe('Closed', () => {
market.tradableInstrument.instrument.name,
MarketStateMapping[market.state],
'3 days ago',
'-',
/* eslint-disable @typescript-eslint/no-non-null-assertion */
addDecimalsFormatNumber(marketsData.bestBidPrice, market.decimalPlaces),
addDecimalsFormatNumber(
@@ -215,6 +256,7 @@ describe('Closed', () => {
addDecimalsFormatNumber(marketsData!.markPrice, market.decimalPlaces),
/* eslint-enable @typescript-eslint/no-non-null-assertion */
addDecimalsFormatNumber(property.value, market.decimalPlaces),
addDecimalsFormatNumber(position.realisedPNL, market.decimalPlaces),
market.tradableInstrument.instrument.product.settlementAsset.symbol,
'', // actions row
];
@@ -273,17 +315,20 @@ describe('Closed', () => {
};
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[mixedMarketsMock, marketsDataMock, oracleDataMock]}
<MockedProvider
mocks={[
mixedMarketsMock,
marketsDataMock,
positionsMock,
oracleDataMock,
]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
);
});
@@ -314,69 +359,4 @@ describe('Closed', () => {
});
expect(cells).toEqual(expectedRows.map((m) => m.node.id));
});
it('successor marked should be visible', async () => {
const mixedMarkets = [
{
__typename: 'MarketEdge' as const,
node: createMarketFragment({
id: 'include-0',
state: MarketState.STATE_SETTLED,
successorMarketID: 'successorMarketID',
}),
},
{
__typename: 'MarketEdge' as const,
node: {
...createMarketFragment({
id: 'successorMarketID',
state: MarketState.STATE_ACTIVE,
}),
tradableInstrument: {
...createMarketFragment().tradableInstrument,
instrument: {
...createMarketFragment().tradableInstrument.instrument,
id: 'successorAssset',
name: 'Successor Market Name',
code: 'SuccessorCode',
},
},
},
},
];
const mixedMarketsMock: MockedResponse<MarketsQuery> = {
request: {
query: MarketsDocument,
},
result: {
data: {
marketsConnection: {
__typename: 'MarketConnection',
edges: mixedMarkets,
},
},
},
};
render(
<MemoryRouter>
<MockedProvider
mocks={[mixedMarketsMock, marketsDataMock, oracleDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
);
await waitFor(() => {
expect(
screen.getByRole('button', { name: 'SuccessorCode' })
).toBeInTheDocument();
});
});
});
+38 -40
View File
@@ -4,11 +4,7 @@ import type {
VegaICellRendererParams,
VegaValueFormatterParams,
} from '@vegaprotocol/datagrid';
import {
AgGridLazy as AgGrid,
COL_DEFS,
MarketNameCell,
} from '@vegaprotocol/datagrid';
import { AgGridLazy as AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
import { useMemo } from 'react';
import { t } from '@vegaprotocol/i18n';
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
@@ -16,6 +12,7 @@ import {
addDecimalsFormatNumber,
getMarketExpiryDate,
} from '@vegaprotocol/utils';
import { usePositionsQuery } from '@vegaprotocol/positions';
import type {
DataSourceFilterFragment,
MarketMaybeWithData,
@@ -23,14 +20,13 @@ import type {
import {
MarketActionsDropdown,
closedMarketsWithDataProvider,
marketProvider,
} from '@vegaprotocol/markets';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import type { ColDef } from 'ag-grid-community';
import { SettlementDateCell } from './settlement-date-cell';
import { SettlementPriceCell } from './settlement-price-cell';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
type SettlementAsset =
MarketMaybeWithData['tradableInstrument']['instrument']['product']['settlementAsset'];
@@ -51,18 +47,33 @@ interface Row {
setlementDataSourceFilter: DataSourceFilterFragment | undefined;
tradingTerminationOracleId: string;
settlementAsset: SettlementAsset;
successorMarketID: string | undefined | null;
realisedPNL: string | undefined;
}
export const Closed = () => {
const { data: marketData, error } = useDataProvider({
const { pubKey } = useVegaWallet();
const {
data: marketData,
error,
reload,
} = useDataProvider({
dataProvider: closedMarketsWithDataProvider,
variables: undefined,
});
const { data: positionData } = usePositionsQuery({
variables: {
partyIds: pubKey ? [pubKey] : [],
},
skip: !pubKey,
});
// find a position for each market and add the realised pnl to
// a normalized object
const rowData = compact(marketData).map((market) => {
const position = positionData?.positions?.edges?.find((edge) => {
return edge.node.market.id === market.id;
});
const instrument = market.tradableInstrument.instrument;
const spec =
@@ -97,46 +108,26 @@ export const Closed = () => {
tradingTerminationOracleId:
instrument.product.dataSourceSpecForTradingTermination.id,
settlementAsset: instrument.product.settlementAsset,
successorMarketID: market.successorMarketID,
realisedPNL: position?.node.realisedPNL,
};
return row;
});
return (
<div className="h-full relative">
<ClosedMarketsDataGrid rowData={rowData} error={error} />
<ClosedMarketsDataGrid rowData={rowData} error={error} reload={reload} />
</div>
);
};
export const SuccessorMarketRenderer = ({
value,
}: VegaICellRendererParams<Row, 'successorMarketID'>) => {
const { data } = useDataProvider({
dataProvider: marketProvider,
variables: {
marketId: value || '',
},
skip: !value,
});
const onMarketClick = useMarketClickHandler();
return data ? (
<MarketNameCell
value={data.tradableInstrument.instrument.code}
data={data}
onMarketClick={onMarketClick}
/>
) : (
' - '
);
};
const ClosedMarketsDataGrid = ({
rowData,
error,
reload,
}: {
rowData: Row[];
error: Error | undefined;
reload: () => void;
}) => {
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
const colDefs = useMemo(() => {
@@ -208,11 +199,6 @@ const ClosedMarketsDataGrid = ({
},
},
},
{
headerName: t('Successor market'),
field: 'successorMarketID',
cellRenderer: 'SuccessorMarketRenderer',
},
{
headerName: t('Best bid'),
field: 'bestBidPrice',
@@ -268,11 +254,25 @@ const ClosedMarketsDataGrid = ({
/>
),
},
{
headerName: t('Realised PNL'),
field: 'realisedPNL',
cellClass: 'font-mono ag-right-aligned-cell',
type: 'numericColumn',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<Row, 'realisedPNL'>) => {
if (!value || !data) return '-';
return addDecimalsFormatNumber(value, data.decimalPlaces);
},
},
{
headerName: t('Settlement asset'),
field: 'settlementAsset',
cellRenderer: ({
value,
data,
}: VegaValueFormatterParams<Row, 'settlementAsset'>) => (
<button
className="underline"
@@ -311,9 +311,7 @@ const ClosedMarketsDataGrid = ({
defaultColDef={{
resizable: true,
minWidth: 100,
flex: 1,
}}
components={{ SuccessorMarketRenderer }}
overlayNoRowsTemplate={error ? error.message : t('No markets')}
/>
);
@@ -15,7 +15,7 @@ export const MarketsPage = () => {
updateTitle(titlefy(['Markets']));
}, [updateTitle]);
return (
<div className="h-full pt-0.5 pb-3 px-1.5">
<div className="h-full pt-0.5 pb-3 px-1">
<div className="h-full my-1 border border-default rounded-sm">
<Tabs storageKey="console-markets">
<Tab id="all-markets" name={t('All markets')}>
@@ -54,7 +54,7 @@ export const Portfolio = () => {
const onMarketClick = useMarketClickHandler(true);
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col';
const wrapperClasses = 'py-0.5 h-full max-h-full flex flex-col';
return (
<div className={wrapperClasses}>
<ResizableGrid vertical onChange={handleOnLayoutChange}>
+10 -8
View File
@@ -12,17 +12,19 @@ export const Header = ({ title, children }: TradeMarketHeaderProps) => {
'grid',
'grid-rows-[min-content_min-content]',
'xl:grid-cols-[min-content_1fr]',
'border-b border-default',
'lg:border-x border-b border-default',
'bg-vega-clight-800 dark:bg-vega-cdark-800'
);
return (
<header className={headerClasses}>
<div className="flex flex-col justify-center items-start pl-3 lg:pl-4 pt-2 xl:pb-2 pb-0">
{title}
</div>
<div data-testid="header-summary" className="min-w-0">
<div className="px-3 lg:px-4 py-2 flex flex-nowrap gap-4 items-center text-xs overflow-x-auto">
{children}
<header className="lg:px-1">
<div className={headerClasses}>
<div className="hidden lg:flex flex-col justify-center pl-4 pt-2 xl:pb-2 pb-0">
{title}
</div>
<div data-testid="header-summary" className="min-w-0">
<div className="px-4 py-2 flex flex-nowrap gap-4 items-center text-xs overflow-x-auto">
{children}
</div>
</div>
</div>
</header>
+3 -3
View File
@@ -1,10 +1,10 @@
export const WalletIcon = ({ className }: { className?: string }) => {
export const WalletIcon = () => {
return (
<svg
width="26"
width="22"
height="18"
viewBox="0 0 26 18"
className={className}
className="fill-current stroke-none"
data-testid="wallet-icon"
>
<path d="M4.77437 17.7499H4.74987C3.6504 17.7368 2.77439 16.8489 2.77439 15.7772V12.8495V12.6343L2.5615 12.6023C1.59672 12.4575 0.849609 11.6116 0.849609 10.6266V7.40064C0.849609 6.39018 1.59509 5.56985 2.56147 5.4249L2.77439 5.39297V5.17767V2.24998C2.77439 1.14102 3.66537 0.25 4.77437 0.25H23.7501C24.8591 0.25 25.7501 1.14098 25.7501 2.24998V15.7499C25.7501 16.8588 24.8591 17.7499 23.7501 17.7499H4.77437ZM4.44917 12.5992H4.19917L4.77441 16.075V16.325H4.77466H23.7502C24.0778 16.325 24.3254 16.0777 24.3254 15.7497V2.24984C24.3254 1.9222 24.0782 1.6746 23.7502 1.6746H4.77441C4.44677 1.6746 4.19917 1.92182 4.19917 2.24984V5.12306V5.37306H4.44917H7.0244C8.51139 5.37306 9.67508 6.56094 9.67508 8.02374V9.94852C9.67508 11.4355 8.4872 12.5992 7.0244 12.5992H4.44917ZM2.84963 6.8253C2.52199 6.8253 2.27439 7.07253 2.27439 7.40054V10.6264C2.27439 10.9541 2.52161 11.2017 2.84962 11.2017L7.02419 11.2019C7.73619 11.2019 8.25009 10.6515 8.25009 9.97598V8.0512C8.25009 7.3392 7.69976 6.8253 7.0242 6.8253H2.84963Z" />
@@ -1,9 +1,9 @@
import { Outlet, Routes, Route } from 'react-router-dom';
import { Outlet, Route, Routes } from 'react-router-dom';
import { Sidebar, SidebarContent, useSidebar } from '../sidebar';
import classNames from 'classnames';
import { Navbar } from '../navbar';
import { NavHeader } from '../nav-header';
import { Routes as AppRoutes } from '../../pages/client-router';
import { MarketHeader } from '../market-header';
import { LiquidityHeader } from '../liquidity-header';
export const LayoutWithSidebar = () => {
const sidebarView = useSidebar((store) => store.view);
@@ -11,36 +11,49 @@ export const LayoutWithSidebar = () => {
const gridClasses = classNames(
'h-full relative z-0 grid',
'grid-rows-[min-content_1fr]',
'grid-cols-[1fr_45px]',
'grid-rows-[min-content_1fr_min-content]',
'lg:grid-rows-[min-content_1fr]',
'lg:grid-cols-[1fr_350px_45px]'
);
return (
<div className={gridClasses}>
<div className="col-span-full">
<Routes>
<Route path={AppRoutes.MARKET} element={<MarketHeader />} />
<Route path={AppRoutes.LIQUIDITY} element={<LiquidityHeader />} />
</Routes>
<div className="col-start-1 col-span-full">
<Navbar>
<Routes>
<Route
path={AppRoutes.MARKETS}
// render nothing for markets/all, otherwise markets/:marketId will match with markets/all
element={null}
/>
<Route path={AppRoutes.MARKET} element={<NavHeader />} />
</Routes>
</Navbar>
</div>
<main
<section
className={classNames('col-start-1 col-end-1', {
'lg:col-end-3': !sidebarOpen,
'hidden lg:block lg:col-end-2': sidebarOpen,
})}
>
<Outlet />
</main>
<aside
</section>
<div
// min-h-0 is needed as this element is part of a grid, we want the content to be scrollable, without it it will push the grid element taller
className={classNames('col-start-1 lg:col-start-2 min-h-0', {
hidden: !sidebarOpen,
})}
>
<SidebarContent />
</aside>
<div className="col-start-2 lg:col-start-3 bg-vega-clight-800 dark:bg-vega-cdark-800 border-l border-default">
</div>
<div
className={classNames(
'bg-vega-clight-800 dark:bg-vega-cdark-800',
'border-t lg:border-l lg:border-t-0 border-default',
'row-start-3 col-start-1 cols-span-full',
'lg:row-start-2 lg:row-span-full lg:col-start-3'
)}
>
<Sidebar />
</div>
</div>
@@ -1 +0,0 @@
export * from './liquidity-header';
@@ -1,107 +0,0 @@
import {
tooltipMapping,
useMarket,
useStaticMarketData,
} from '@vegaprotocol/markets';
import { Header, HeaderStat, HeaderTitle } from '../header';
import {
addDecimalsFormatNumber,
formatNumberPercentage,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { ExternalLink, Indicator } from '@vegaprotocol/ui-toolkit';
import { DocsLinks } from '@vegaprotocol/environment';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import { useCheckLiquidityStatus } from '@vegaprotocol/liquidity';
import { useParams } from 'react-router-dom';
export const LiquidityHeader = () => {
const { marketId } = useParams();
const { data: market } = useMarket(marketId);
const { data: marketData } = useStaticMarketData(marketId);
const targetStake = marketData?.targetStake;
const suppliedStake = marketData?.suppliedStake;
const assetDecimalPlaces =
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
const symbol =
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
const { params } = useNetworkParams([
NetworkParams.market_liquidity_stakeToCcyVolume,
NetworkParams.market_liquidity_targetstake_triggering_ratio,
]);
const triggeringRatio =
params.market_liquidity_targetstake_triggering_ratio || '1';
const { percentage, status } = useCheckLiquidityStatus({
suppliedStake: suppliedStake || 0,
targetStake: targetStake || 0,
triggeringRatio,
});
console.log(market);
return (
<Header
title={
market?.tradableInstrument.instrument.code &&
marketId && (
<HeaderTitle>
{market.tradableInstrument.instrument.code &&
t(
'%s liquidity provision',
market.tradableInstrument.instrument.code
)}
</HeaderTitle>
)
}
>
<HeaderStat
heading={t('Target stake')}
description={tooltipMapping['targetStake']}
testId="target-stake"
>
<div>
{targetStake
? `${addDecimalsFormatNumber(
targetStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat
heading={t('Supplied stake')}
description={tooltipMapping['suppliedStake']}
testId="supplied-stake"
>
<div>
{suppliedStake
? `${addDecimalsFormatNumber(
suppliedStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat heading={t('Liquidity supplied')} testId="liquidity-supplied">
<Indicator variant={status} /> {formatNumberPercentage(percentage, 2)}
</HeaderStat>
<HeaderStat heading={t('Market ID')} testId="liquidity-market-id">
<div className="break-word">{marketId}</div>
</HeaderStat>
<HeaderStat heading={t('Learn more')} testId="liquidity-learn-more">
{DocsLinks ? (
<ExternalLink href={DocsLinks.LIQUIDITY}>
{t('Providing liquidity')}
</ExternalLink>
) : (
(null as React.ReactNode)
)}
</HeaderStat>
</Header>
);
};
@@ -1 +0,0 @@
export * from './market-successor-banner';
@@ -1,188 +0,0 @@
import { render, screen } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import * as dataProviders from '@vegaprotocol/data-provider';
import { MarketSuccessorBanner } from './market-successor-banner';
import * as Types from '@vegaprotocol/types';
import * as allUtils from '@vegaprotocol/utils';
import type { Market } from '@vegaprotocol/markets';
import type { PartialDeep } from 'type-fest';
const market = {
id: 'marketId',
tradableInstrument: {
instrument: {
metadata: {
tags: [],
},
},
},
marketTimestamps: {
close: null,
},
successorMarketID: 'successorMarketID',
} as unknown as Market;
let mockDataSuccessorMarket: PartialDeep<Market> | null = null;
jest.mock('@vegaprotocol/data-provider', () => ({
...jest.requireActual('@vegaprotocol/data-provider'),
useDataProvider: jest.fn().mockImplementation((args) => {
if (args.skip) {
return {
data: null,
error: null,
};
}
return {
data: mockDataSuccessorMarket,
error: null,
};
}),
}));
jest.mock('@vegaprotocol/utils', () => ({
...jest.requireActual('@vegaprotocol/utils'),
getMarketExpiryDate: jest.fn(),
}));
let mockCandles = {};
jest.mock('@vegaprotocol/markets', () => ({
...jest.requireActual('@vegaprotocol/markets'),
useCandles: () => mockCandles,
}));
describe('MarketSuccessorBanner', () => {
beforeEach(() => {
jest.clearAllMocks();
mockDataSuccessorMarket = {
id: 'successorMarketID',
state: Types.MarketState.STATE_ACTIVE,
tradingMode: Types.MarketTradingMode.TRADING_MODE_CONTINUOUS,
tradableInstrument: {
instrument: {
name: 'Successor Market Name',
},
},
};
});
describe('should be hidden', () => {
it('when no market', () => {
const { container } = render(<MarketSuccessorBanner market={null} />, {
wrapper: MockedProvider,
});
expect(container).toBeEmptyDOMElement();
});
it('when no successorMarketID', () => {
const amendedMarket = {
...market,
successorMarketID: null,
};
const { container } = render(
<MarketSuccessorBanner market={amendedMarket} />,
{
wrapper: MockedProvider,
}
);
expect(container).toBeEmptyDOMElement();
expect(dataProviders.useDataProvider).lastCalledWith(
expect.objectContaining({ skip: true })
);
});
it('no successor market data', () => {
mockDataSuccessorMarket = null;
const { container } = render(<MarketSuccessorBanner market={market} />, {
wrapper: MockedProvider,
});
expect(container).toBeEmptyDOMElement();
expect(dataProviders.useDataProvider).lastCalledWith(
expect.objectContaining({
variables: { marketId: 'successorMarketID' },
skip: false,
})
);
});
it('successor market not in continuous mode', () => {
mockDataSuccessorMarket = {
...mockDataSuccessorMarket,
tradingMode: Types.MarketTradingMode.TRADING_MODE_NO_TRADING,
};
const { container } = render(<MarketSuccessorBanner market={market} />, {
wrapper: MockedProvider,
});
expect(container).toBeEmptyDOMElement();
expect(dataProviders.useDataProvider).lastCalledWith(
expect.objectContaining({
variables: { marketId: 'successorMarketID' },
skip: false,
})
);
expect(allUtils.getMarketExpiryDate).toHaveBeenCalled();
});
it('successor market is not active', () => {
mockDataSuccessorMarket = {
...mockDataSuccessorMarket,
state: Types.MarketState.STATE_PENDING,
};
const { container } = render(<MarketSuccessorBanner market={market} />, {
wrapper: MockedProvider,
});
expect(container).toBeEmptyDOMElement();
expect(dataProviders.useDataProvider).lastCalledWith(
expect.objectContaining({
variables: { marketId: 'successorMarketID' },
skip: false,
})
);
expect(allUtils.getMarketExpiryDate).toHaveBeenCalled();
});
});
describe('should be displayed', () => {
it('should be rendered', () => {
render(<MarketSuccessorBanner market={market} />, {
wrapper: MockedProvider,
});
expect(
screen.getByText('This market has been succeeded')
).toBeInTheDocument();
expect(
screen.getByRole('link', { name: 'Successor Market Name' })
).toHaveAttribute('href', '/#/markets/successorMarketID');
});
it('should display optionally successor volume', () => {
mockDataSuccessorMarket = {
...mockDataSuccessorMarket,
positionDecimalPlaces: 3,
};
mockCandles = {
oneDayCandles: [
{ volume: 123 },
{ volume: 456 },
{ volume: 789 },
{ volume: 99999 },
],
};
render(<MarketSuccessorBanner market={market} />, {
wrapper: MockedProvider,
});
expect(screen.getByText('has 101.367 24h vol.')).toBeInTheDocument();
});
it('should display optionally duration', () => {
jest
.spyOn(allUtils, 'getMarketExpiryDate')
.mockReturnValue(
new Date(Date.now() + 24 * 60 * 60 * 1000 + 60 * 1000)
);
render(<MarketSuccessorBanner market={market} />, {
wrapper: MockedProvider,
});
expect(
screen.getByText(/^This market expires in 1 day/)
).toBeInTheDocument();
});
});
});
@@ -1,115 +0,0 @@
import { useState } from 'react';
import { isBefore, formatDuration, intervalToDuration } from 'date-fns';
import { useDataProvider } from '@vegaprotocol/data-provider';
import type { Market } from '@vegaprotocol/markets';
import {
calcCandleVolume,
marketProvider,
useCandles,
} from '@vegaprotocol/markets';
import {
ExternalLink,
Intent,
NotificationBanner,
} from '@vegaprotocol/ui-toolkit';
import {
addDecimalsFormatNumber,
getMarketExpiryDate,
isNumeric,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import * as Types from '@vegaprotocol/types';
const getExpiryDate = (tags: string[], close?: string): Date | null => {
const expiryDate = getMarketExpiryDate(tags);
return expiryDate || (close && new Date(close)) || null;
};
export const MarketSuccessorBanner = ({
market,
}: {
market: Market | null;
}) => {
const { data: successorData } = useDataProvider({
dataProvider: marketProvider,
variables: {
marketId: market?.successorMarketID || '',
},
skip: !market?.successorMarketID,
});
const [visible, setVisible] = useState(true);
const expiry = market
? getExpiryDate(
market.tradableInstrument.instrument.metadata.tags || [],
market.marketTimestamps.close
)
: null;
const duration =
expiry && isBefore(new Date(), expiry)
? intervalToDuration({ start: new Date(), end: expiry })
: null;
const isInContinuesMode =
successorData?.state === Types.MarketState.STATE_ACTIVE &&
successorData?.tradingMode ===
Types.MarketTradingMode.TRADING_MODE_CONTINUOUS;
const { oneDayCandles } = useCandles({
marketId: successorData?.id,
});
const candleVolume = oneDayCandles?.length
? calcCandleVolume(oneDayCandles)
: null;
const successorVolume =
candleVolume && isNumeric(successorData?.positionDecimalPlaces)
? addDecimalsFormatNumber(
candleVolume,
successorData?.positionDecimalPlaces as number
)
: null;
if (isInContinuesMode && visible) {
return (
<NotificationBanner
intent={Intent.Primary}
onClose={() => {
setVisible(false);
}}
>
<div className="uppercase mb-1">
{t('This market has been succeeded')}
</div>
<div>
{duration && (
<span>
{t('This market expires in %s.', [
formatDuration(duration, {
format: [
'years',
'months',
'weeks',
'days',
'hours',
'minutes',
],
}),
])}
</span>
)}{' '}
{t('The successor market')}{' '}
<ExternalLink href={`/#/markets/${successorData?.id}`}>
{successorData?.tradableInstrument.instrument.name}
</ExternalLink>
{successorVolume && (
<span> {t('has %s 24h vol.', [successorVolume])}</span>
)}
</div>
</NotificationBanner>
);
}
return null;
};
@@ -1 +0,0 @@
export * from './market-header';
@@ -1,33 +0,0 @@
import { Popover, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { Header, HeaderTitle } from '../header';
import { useParams } from 'react-router-dom';
import { MarketSelector } from '../../components/market-selector/market-selector';
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
import { useMarket } from '@vegaprotocol/markets';
export const MarketHeader = () => {
const { marketId } = useParams();
const { data } = useMarket(marketId);
if (!data) return null;
return (
<Header
title={
<Popover
trigger={
<HeaderTitle>
{data.tradableInstrument.instrument.code}
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={14} />
</HeaderTitle>
}
alignOffset={-10}
>
<MarketSelector currentMarketId={marketId} />
</Popover>
}
>
<MarketHeaderStats market={data} />
</Header>
);
};
@@ -1,2 +0,0 @@
export * from './market-selector';
export * from './market-selector-item';
@@ -0,0 +1 @@
export * from './nav-header';
@@ -0,0 +1,28 @@
import { Popover, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { MarketSelector } from '../../client-pages/market/market-selector';
import { useMarket } from '@vegaprotocol/markets';
import { t } from '@vegaprotocol/i18n';
import { useParams } from 'react-router-dom';
/**
* This is only rendered for the mobile navigation
*/
export const NavHeader = () => {
const { marketId } = useParams();
const { data } = useMarket(marketId);
if (!marketId) return null;
return (
<Popover
trigger={
<h1 className="flex gap-4 items-center text-default text-lg whitespace-nowrap xl:pr-4 xl:border-r border-default">
{data ? data.tradableInstrument.instrument.code : t('Select market')}
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} />
</h1>
}
>
<MarketSelector currentMarketId={marketId} />
</Popover>
);
};
+276 -2
View File
@@ -1,4 +1,4 @@
import type { ComponentProps, ReactNode } from 'react';
import { useState, type ComponentProps, type ReactNode } from 'react';
import {
DApp,
NetworkSwitcher,
@@ -6,6 +6,7 @@ import {
useEnvironment,
useLinks,
DocsLinks,
Networks,
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import { useGlobalStore } from '../../stores';
@@ -21,15 +22,253 @@ import {
NavigationContent,
VegaIconNames,
VegaIcon,
VLogo,
} from '@vegaprotocol/ui-toolkit';
import * as N from '@radix-ui/react-navigation-menu';
import * as D from '@radix-ui/react-dialog';
import { NavLink } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import {
ProtocolUpgradeCountdown,
ProtocolUpgradeCountdownMode,
} from '@vegaprotocol/proposals';
import classNames from 'classnames';
import { VegaWalletMenu } from '../vega-wallet';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { WalletIcon } from '../icons/wallet';
export const Navbar = ({
type MenuState = 'wallet' | 'nav' | null;
export const Navbar = ({ children }: { children?: ReactNode }) => {
const [menu, setMenu] = useState<MenuState>(null);
const { pubKey } = useVegaWallet();
const isConnected = pubKey !== null;
const openVegaWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
return (
<N.Root className="text-vega-clight-200 dark:text-vega-cdark-200 ">
<div className="flex items-center gap-2 h-10 px-3 lg:pl-5 pr-2 border-b border-default bg-vega-clight-800 dark:bg-vega-cdark-800">
<div className="pr-2">
<VLogo className="w-4 text-default" />
</div>
<div className="lg:hidden">{children}</div>
<div className="hidden lg:block">
<NavbarMenu onClick={() => setMenu(null)} />
</div>
<div className="ml-auto flex justify-end items-center gap-2">
<button
className="lg:hidden flex items-center p-1 rounded hover:bg-vega-clight-500 dark:hover:bg-vega-cdark-500"
onClick={() => {
if (isConnected) {
setMenu((x) => (x === 'wallet' ? null : 'wallet'));
} else {
openVegaWalletDialog();
}
}}
>
<WalletIcon />
</button>
<button
className="lg:hidden flex itesm-center p-1 rounded hover:bg-vega-clight-500 dark:hover:bg-vega-cdark-500"
onClick={() => {
setMenu((x) => (x === 'nav' ? null : 'nav'));
}}
>
<BurgerIcon />
</button>
<div className="hidden lg:block">
<VegaWalletConnectButton />
</div>
</div>
</div>
{menu !== null && (
<D.Root
open={menu !== null}
onOpenChange={(open) => setMenu((x) => (open ? x : null))}
>
<D.Overlay
className="fixed inset-0 dark:bg-black/80 bg-black/50 z-20"
data-testid="dialog-overlay"
/>
<D.Content className="fixed top-0 right-0 z-20 w-3/4 h-screen border-l border-default bg-vega-clight-700 dark:bg-vega-cdark-700">
<div className="flex justify-end items-center h-10 p-1">
<button
onClick={() => setMenu(null)}
className="flex flex-col justify-center p-2 hover:bg-vega-clight-500 dark:hover:bg-vega-cdark-500 rounded"
>
<VegaIcon name={VegaIconNames.CROSS} size={24} />
</button>
</div>
{menu === 'nav' && <NavbarMenu onClick={() => setMenu(null)} />}
{menu === 'wallet' && <VegaWalletMenu setMenu={setMenu} />}
</D.Content>
</D.Root>
)}
</N.Root>
);
};
const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
const { VEGA_ENV, VEGA_NETWORKS, GITHUB_FEEDBACK_URL } = useEnvironment();
const marketId = useGlobalStore((store) => store.marketId);
const tradingPath = marketId
? Links[Routes.MARKET](marketId)
: Links[Routes.MARKET]();
return (
<N.List className="lg:flex gap-4">
<N.Item>
<NavbarTrigger>
{envNameMapping[VEGA_ENV]}
<NavbarDivider />
</NavbarTrigger>
<NavbarContent>
<ul className="lg:px-4 lg:py-2">
{[Networks.MAINNET, Networks.TESTNET].map((n) => {
const url = VEGA_NETWORKS[n];
if (!url) return;
return (
<li key={n}>
<NavbarLink to={url}>{envNameMapping[n]}</NavbarLink>
</li>
);
})}
</ul>
</NavbarContent>
</N.Item>
<NavbarDivider />
<N.Item>
<NavbarLink to={Links[Routes.MARKETS]()} onClick={onClick}>
{t('Markets')}
</NavbarLink>
</N.Item>
<N.Item>
<NavbarLink to={tradingPath} onClick={onClick}>
{t('Trading')}
</NavbarLink>
</N.Item>
<N.Item>
<NavbarLink to={Links[Routes.PORTFOLIO]()} onClick={onClick}>
{t('Portfolio')}
</NavbarLink>
</N.Item>
<N.Item>
<NavbarTrigger>{t('Resources')}</NavbarTrigger>
<NavbarContent>
<ul className="lg:px-4 lg:py-2">
{DocsLinks?.NEW_TO_VEGA && (
<li>
<NavbarLink to={DocsLinks?.NEW_TO_VEGA}>{t('Docs')}</NavbarLink>
</li>
)}
{GITHUB_FEEDBACK_URL && (
<li>
<NavbarLink to={GITHUB_FEEDBACK_URL}>
{t('Give Feedback')}
</NavbarLink>
</li>
)}
<li>
<NavbarLink to={Links[Routes.DISCLAIMER]()} onClick={onClick}>
{t('Disclaimer')}
</NavbarLink>
</li>
</ul>
</NavbarContent>
</N.Item>
</N.List>
);
};
const NavbarTrigger = ({ children }: { children: ReactNode }) => {
return (
<N.Trigger
onPointerMove={preventHover}
onPointerLeave={preventHover}
className={classNames(
'relative flex items-center gap-2 py-2 px-6 lg:px-0',
'text-lg lg:text-base hover:text-vega-clight-50 dark:hover:text-vega-cdark-50'
)}
>
{children}
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={14} />
</N.Trigger>
);
};
const NavbarLink = ({
children,
to,
onClick,
}: {
children: ReactNode;
to: string;
onClick?: () => void;
}) => {
return (
<N.Link asChild={true}>
<NavLink
to={to}
className="relative block lg:inline-block py-2 px-6 lg:px-0 text-lg lg:text-base"
onClick={onClick}
>
{({ isActive }) => {
const borderClasses = {
'border-b-2': true,
'border-transparent': !isActive,
'border-vega-yellow': isActive,
};
return (
<>
<span
className={classNames(
'hover:text-vega-clight-50 dark:hover:text-vega-cdark-50 lg:border-0',
borderClasses,
{
'text-vega-clight-50 dark:text-vega-cdark-50': isActive,
}
)}
>
{children}
</span>
<span
className={classNames(
'hidden lg:block absolute left-0 bottom-0 w-full h-0',
borderClasses
)}
/>
</>
);
}}
</NavLink>
</N.Link>
);
};
const NavbarContent = ({ children }: { children: ReactNode }) => {
return (
<N.Content
className={classNames(
'lg:absolute pl-2 lg:pl-0 z-20 lg:min-w-[290px]',
'lg:bg-vega-clight-800 lg:dark:bg-vega-cdark-800',
'lg:border border-default lg:rounded'
)}
onPointerEnter={preventHover}
onPointerLeave={preventHover}
>
{children}
</N.Content>
);
};
// https://github.com/radix-ui/primitives/issues/1630
const preventHover = (e: any) => {
e.preventDefault();
};
export const Navbar2 = ({
theme = 'system',
}: {
theme: ComponentProps<typeof Navigation>['theme'];
@@ -42,6 +281,17 @@ export const Navbar = ({
? Links[Routes.MARKET](marketId)
: Links[Routes.MARKET]();
// return (
// <nav className="flex items-center gap-4 py-2">
// <div>Network switcher</div>
// <NavLink to={Links[Routes.MARKETS]()}>Markets</NavLink>
// <NavLink to={tradingPath} end>
// Trading
// </NavLink>
// <NavLink to={Links[Routes.PORTFOLIO]()}>Portfolio</NavLink>
// </nav>
// );
return (
<Navigation
appName="console"
@@ -138,3 +388,27 @@ const NavExternalLink = ({
</ExternalLink>
);
};
const BurgerIcon = () => (
<svg width="16" height="16" viewBox="0 0 16 16" className="stroke-current">
<line x1={0.5} x2={15.5} y1={3.5} y2={3.5} />
<line x1={0.5} x2={15.5} y1={11.5} y2={11.5} />
</svg>
);
const NavbarDivider = () => {
return (
<li className="lg:py-2" role="separator">
<div className="h-px lg:h-full w-full lg:w-px bg-vega-clight-600 dark:bg-vega-cdark-600" />
</li>
);
};
const envNameMapping: Record<Networks, string> = {
[Networks.VALIDATOR_TESTNET]: t('VALIDATOR_TESTNET'),
[Networks.CUSTOM]: t('Custom'),
[Networks.DEVNET]: t('Devnet'),
[Networks.STAGNET1]: t('Stagnet'),
[Networks.TESTNET]: t('Fairground testnet'),
[Networks.MAINNET]: t('Mainnet'),
};

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