Improvements from the PR
This commit is contained in:
commit
2d635808de
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"root": true,
|
"root": true,
|
||||||
"ignorePatterns": ["**/*"],
|
"ignorePatterns": ["**/*"],
|
||||||
"plugins": ["@nrwl/nx"],
|
"plugins": ["@nrwl/nx", "eslint-plugin-unicorn"],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
@ -18,6 +18,13 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"unicorn/filename-case": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"case": "kebabCase",
|
||||||
|
"ignore": ["\\[[a-zA-Z]+\\]\\.page"]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
55
.github/workflows/cypress.yml
vendored
Normal file
55
.github/workflows/cypress.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
name: Cypress tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
master:
|
||||||
|
name: Run end-to-end tests - main
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
||||||
|
uses: nrwl/nx-set-shas@v2
|
||||||
|
with:
|
||||||
|
main-branch-name: master
|
||||||
|
- name: Use Node.js 16
|
||||||
|
id: Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
- name: Install root dependencies
|
||||||
|
run: yarn install
|
||||||
|
- name: Run Cypress tests
|
||||||
|
run: npx nx affected:e2e --parallel=5 --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||||
|
pr:
|
||||||
|
name: Run end-to-end tests - PR
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.ref }}
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
||||||
|
uses: nrwl/nx-set-shas@v2
|
||||||
|
with:
|
||||||
|
main-branch-name: master
|
||||||
|
- name: Use Node.js 16
|
||||||
|
id: Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
- name: Install root dependencies
|
||||||
|
run: yarn install
|
||||||
|
- name: Run Cypress tests
|
||||||
|
run: npx nx affected:e2e --parallel=5 --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
|
6
.github/workflows/publish.yml
vendored
6
.github/workflows/publish.yml
vendored
@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
name: Publish
|
name: Publish
|
||||||
|
|
||||||
"on":
|
'on':
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "v[0-9]+.[0-9]+.[0-9]+"
|
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||||
- "v[0-9]+.[0-9]+.[0-9]+-pre[0-9]+"
|
- 'v[0-9]+.[0-9]+.[0-9]+-pre[0-9]+'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
stories: [],
|
stories: [],
|
||||||
addons: ['@storybook/addon-essentials'],
|
addons: ['@storybook/addon-essentials', '@storybook/addon-a11y'],
|
||||||
// uncomment the property below if you want to apply some webpack config globally
|
// uncomment the property below if you want to apply some webpack config globally
|
||||||
// webpackFinal: async (config, { configType }) => {
|
// webpackFinal: async (config, { configType }) => {
|
||||||
// // Make whatever fine-grained changes you need that should apply to all storybook configs
|
// // Make whatever fine-grained changes you need that should apply to all storybook configs
|
||||||
|
@ -2,3 +2,11 @@ NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-
|
|||||||
NX_TENDERMINT_URL = "https://lb.testnet.vega.xyz/tm"
|
NX_TENDERMINT_URL = "https://lb.testnet.vega.xyz/tm"
|
||||||
NX_TENDERMINT_WEBSOCKET_URL = "wss://lb.testnet.vega.xyz/tm/websocket"
|
NX_TENDERMINT_WEBSOCKET_URL = "wss://lb.testnet.vega.xyz/tm/websocket"
|
||||||
NX_VEGA_URL = "https://lb.testnet.vega.xyz/query"
|
NX_VEGA_URL = "https://lb.testnet.vega.xyz/query"
|
||||||
|
|
||||||
|
NX_EXPLORER_ASSETS = 1
|
||||||
|
NX_EXPLORER_GENESIS = 1
|
||||||
|
NX_EXPLORER_GOVERNANCE = 1
|
||||||
|
NX_EXPLORER_MARKETS = 1
|
||||||
|
NX_EXPLORER_NETWORK_PARAMETERS = 1
|
||||||
|
NX_EXPLORER_PARTIES = 1
|
||||||
|
NX_EXPLORER_VALIDATORS = 1
|
34
apps/explorer-e2e/.env.devent
Normal file
34
apps/explorer-e2e/.env.devent
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# React Environment Variables
|
||||||
|
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
|
||||||
|
|
||||||
|
# Netlify Environment Variables
|
||||||
|
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
||||||
|
NX_VERSION=$npm_package_version
|
||||||
|
NX_REPOSITORY_URL=$REPOSITORY_URL
|
||||||
|
NX_BRANCH=$BRANCH
|
||||||
|
NX_PULL_REQUEST=$PULL_REQUEST
|
||||||
|
NX_HEAD=$HEAD
|
||||||
|
NX_COMMIT_REF=$COMMIT_REF
|
||||||
|
NX_CONTEXT=$CONTEXT
|
||||||
|
NX_REVIEW_ID=$REVIEW_ID
|
||||||
|
NX_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
|
||||||
|
NX_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
|
||||||
|
NX_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
|
||||||
|
NX_URL=$URL
|
||||||
|
NX_DEPLOY_URL=$DEPLOY_URL
|
||||||
|
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
||||||
|
|
||||||
|
# App configuration variables
|
||||||
|
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
|
||||||
|
NX_TENDERMINT_URL = "https://n04.d.vega.xyz/tm"
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL = "wss://n04.d.vega.xyz/tm/websocket"
|
||||||
|
NX_VEGA_URL = "https://n04.d.vega.xyz/query"
|
||||||
|
|
||||||
|
# App flags
|
||||||
|
NX_EXPLORER_ASSETS = 1
|
||||||
|
NX_EXPLORER_GENESIS = 1
|
||||||
|
NX_EXPLORER_GOVERNANCE = 1
|
||||||
|
NX_EXPLORER_MARKETS = 1
|
||||||
|
NX_EXPLORER_NETWORK_PARAMETERS = 1
|
||||||
|
NX_EXPLORER_PARTIES = 1
|
||||||
|
NX_EXPLORER_VALIDATORS = 1
|
34
apps/explorer-e2e/.env.mainnet
Normal file
34
apps/explorer-e2e/.env.mainnet
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# React Environment Variables
|
||||||
|
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
|
||||||
|
|
||||||
|
# Netlify Environment Variables
|
||||||
|
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
||||||
|
NX_VERSION=$npm_package_version
|
||||||
|
NX_REPOSITORY_URL=$REPOSITORY_URL
|
||||||
|
NX_BRANCH=$BRANCH
|
||||||
|
NX_PULL_REQUEST=$PULL_REQUEST
|
||||||
|
NX_HEAD=$HEAD
|
||||||
|
NX_COMMIT_REF=$COMMIT_REF
|
||||||
|
NX_CONTEXT=$CONTEXT
|
||||||
|
NX_REVIEW_ID=$REVIEW_ID
|
||||||
|
NX_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
|
||||||
|
NX_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
|
||||||
|
NX_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
|
||||||
|
NX_URL=$URL
|
||||||
|
NX_DEPLOY_URL=$DEPLOY_URL
|
||||||
|
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
||||||
|
|
||||||
|
# App configuration variables
|
||||||
|
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
|
||||||
|
NX_TENDERMINT_URL = "https://mainnet-observer-proxy01.ops.vega.xyz/"
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL = "wss://mainnet-observer-proxy01.ops.vega.xyz/websocket"
|
||||||
|
NX_VEGA_URL = "https://api.token.vega.xyz/query"
|
||||||
|
|
||||||
|
# App flags
|
||||||
|
NX_EXPLORER_ASSETS = 1
|
||||||
|
NX_EXPLORER_GENESIS = 1
|
||||||
|
NX_EXPLORER_GOVERNANCE = 1
|
||||||
|
NX_EXPLORER_MARKETS = 1
|
||||||
|
NX_EXPLORER_NETWORK_PARAMETERS = 1
|
||||||
|
NX_EXPLORER_PARTIES = 1
|
||||||
|
NX_EXPLORER_VALIDATORS = 1
|
35
apps/explorer-e2e/.env.stagnet1
Normal file
35
apps/explorer-e2e/.env.stagnet1
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# React Environment Variables
|
||||||
|
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
|
||||||
|
|
||||||
|
# Netlify Environment Variables
|
||||||
|
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
||||||
|
NX_VERSION=$npm_package_version
|
||||||
|
NX_REPOSITORY_URL=$REPOSITORY_URL
|
||||||
|
NX_BRANCH=$BRANCH
|
||||||
|
NX_PULL_REQUEST=$PULL_REQUEST
|
||||||
|
NX_HEAD=$HEAD
|
||||||
|
NX_COMMIT_REF=$COMMIT_REF
|
||||||
|
NX_CONTEXT=$CONTEXT
|
||||||
|
NX_REVIEW_ID=$REVIEW_ID
|
||||||
|
NX_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
|
||||||
|
NX_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
|
||||||
|
NX_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
|
||||||
|
NX_URL=$URL
|
||||||
|
NX_DEPLOY_URL=$DEPLOY_URL
|
||||||
|
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
||||||
|
|
||||||
|
# App configuration variables
|
||||||
|
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
|
||||||
|
NX_TENDERMINT_URL = "https://n03.s.vega.xyz/tm"
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL = "wss://n03.s.vega.xyz/tm/websocket"
|
||||||
|
NX_VEGA_URL = "https://n03.s.vega.xyz/query"
|
||||||
|
|
||||||
|
|
||||||
|
# App flags
|
||||||
|
NX_EXPLORER_ASSETS = 1
|
||||||
|
NX_EXPLORER_GENESIS = 1
|
||||||
|
NX_EXPLORER_GOVERNANCE = 1
|
||||||
|
NX_EXPLORER_MARKETS = 1
|
||||||
|
NX_EXPLORER_NETWORK_PARAMETERS = 1
|
||||||
|
NX_EXPLORER_PARTIES = 1
|
||||||
|
NX_EXPLORER_VALIDATORS = 1
|
34
apps/explorer-e2e/.env.stagnet2
Normal file
34
apps/explorer-e2e/.env.stagnet2
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# React Environment Variables
|
||||||
|
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
|
||||||
|
|
||||||
|
# Netlify Environment Variables
|
||||||
|
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
||||||
|
NX_VERSION=$npm_package_version
|
||||||
|
NX_REPOSITORY_URL=$REPOSITORY_URL
|
||||||
|
NX_BRANCH=$BRANCH
|
||||||
|
NX_PULL_REQUEST=$PULL_REQUEST
|
||||||
|
NX_HEAD=$HEAD
|
||||||
|
NX_COMMIT_REF=$COMMIT_REF
|
||||||
|
NX_CONTEXT=$CONTEXT
|
||||||
|
NX_REVIEW_ID=$REVIEW_ID
|
||||||
|
NX_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
|
||||||
|
NX_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
|
||||||
|
NX_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
|
||||||
|
NX_URL=$URL
|
||||||
|
NX_DEPLOY_URL=$DEPLOY_URL
|
||||||
|
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
||||||
|
|
||||||
|
# App configuration variables
|
||||||
|
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
|
||||||
|
NX_TENDERMINT_URL = "https://n03.stagnet2.vega.xyz/tm"
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL = "wss://n03.stagnet2.vega.xyz/tm/websocket"
|
||||||
|
NX_VEGA_URL = "https://n03.stagnet2.vega.xyz/query"
|
||||||
|
|
||||||
|
# App flags
|
||||||
|
NX_EXPLORER_ASSETS = 1
|
||||||
|
NX_EXPLORER_GENESIS = 1
|
||||||
|
NX_EXPLORER_GOVERNANCE = 1
|
||||||
|
NX_EXPLORER_MARKETS = 1
|
||||||
|
NX_EXPLORER_NETWORK_PARAMETERS = 1
|
||||||
|
NX_EXPLORER_PARTIES = 1
|
||||||
|
NX_EXPLORER_VALIDATORS = 1
|
34
apps/explorer-e2e/.env.testnet
Normal file
34
apps/explorer-e2e/.env.testnet
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# React Environment Variables
|
||||||
|
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
|
||||||
|
|
||||||
|
# Netlify Environment Variables
|
||||||
|
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
||||||
|
NX_VERSION=$npm_package_version
|
||||||
|
NX_REPOSITORY_URL=$REPOSITORY_URL
|
||||||
|
NX_BRANCH=$BRANCH
|
||||||
|
NX_PULL_REQUEST=$PULL_REQUEST
|
||||||
|
NX_HEAD=$HEAD
|
||||||
|
NX_COMMIT_REF=$COMMIT_REF
|
||||||
|
NX_CONTEXT=$CONTEXT
|
||||||
|
NX_REVIEW_ID=$REVIEW_ID
|
||||||
|
NX_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
|
||||||
|
NX_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
|
||||||
|
NX_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
|
||||||
|
NX_URL=$URL
|
||||||
|
NX_DEPLOY_URL=$DEPLOY_URL
|
||||||
|
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
||||||
|
|
||||||
|
# App configuration variables
|
||||||
|
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
|
||||||
|
NX_TENDERMINT_URL = "https://lb.testnet.vega.xyz/tm"
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL = "wss://lb.testnet.vega.xyz/tm/websocket"
|
||||||
|
NX_VEGA_URL = "https://lb.testnet.vega.xyz/query"
|
||||||
|
|
||||||
|
# App flags
|
||||||
|
NX_EXPLORER_ASSETS = 1
|
||||||
|
NX_EXPLORER_GENESIS = 1
|
||||||
|
NX_EXPLORER_GOVERNANCE = 1
|
||||||
|
NX_EXPLORER_MARKETS = 1
|
||||||
|
NX_EXPLORER_NETWORK_PARAMETERS = 1
|
||||||
|
NX_EXPLORER_PARTIES = 1
|
||||||
|
NX_EXPLORER_VALIDATORS = 1
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"baseUrl": "http://localhost:4200",
|
"baseUrl": "http://localhost:4200",
|
||||||
|
"projectId": "et4snf",
|
||||||
"fileServerFolder": ".",
|
"fileServerFolder": ".",
|
||||||
"fixturesFolder": false,
|
"fixturesFolder": false,
|
||||||
"pluginsFile": "./src/plugins/index.js",
|
"pluginsFile": "./src/plugins/index.js",
|
||||||
|
6
apps/explorer-e2e/src/integration/asset-page.feature
Normal file
6
apps/explorer-e2e/src/integration/asset-page.feature
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Feature: Asset Page
|
||||||
|
|
||||||
|
Scenario: Navigate to Asset Page
|
||||||
|
Given I am on the homepage
|
||||||
|
When I navigate to the asset page
|
||||||
|
Then asset page is correctly displayed
|
6
apps/explorer-e2e/src/integration/blocks-page.feature
Normal file
6
apps/explorer-e2e/src/integration/blocks-page.feature
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Feature: Blocks Page
|
||||||
|
|
||||||
|
Scenario: Navigate to blocks page
|
||||||
|
Given I am on the homepage
|
||||||
|
When I navigate to the blocks page
|
||||||
|
Then blocks page is correctly displayed
|
@ -1,4 +1,4 @@
|
|||||||
Feature: Home page
|
Feature: Home page
|
||||||
|
|
||||||
Scenario: Visit Home page
|
Scenario: Visit Home page
|
||||||
Given I go to the homepage
|
Given I am on the homepage
|
||||||
|
6
apps/explorer-e2e/src/integration/markets-page.feature
Normal file
6
apps/explorer-e2e/src/integration/markets-page.feature
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Feature: Markets Page
|
||||||
|
|
||||||
|
Scenario: Navigate to markets page
|
||||||
|
Given I am on the homepage
|
||||||
|
When I navigate to the markets page
|
||||||
|
Then markets page is correctly displayed
|
6
apps/explorer-e2e/src/integration/network-page.feature
Normal file
6
apps/explorer-e2e/src/integration/network-page.feature
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Feature: Network parameters Page
|
||||||
|
|
||||||
|
Scenario: Navigate to network parameters page
|
||||||
|
Given I am on the homepage
|
||||||
|
When I navigate to the transactions page
|
||||||
|
Then transactions page is correctly displayed
|
@ -0,0 +1,6 @@
|
|||||||
|
Feature: Transactions Page
|
||||||
|
|
||||||
|
Scenario: Navigate to transactions page
|
||||||
|
Given I am on the homepage
|
||||||
|
When I navigate to the transactions page
|
||||||
|
Then transactions page is correctly displayed
|
@ -0,0 +1,6 @@
|
|||||||
|
Feature: Validators Page
|
||||||
|
|
||||||
|
Scenario: Navigate to validators page
|
||||||
|
Given I am on the homepage
|
||||||
|
When I navigate to the validators page
|
||||||
|
Then validators page is correctly displayed
|
@ -13,6 +13,7 @@ declare namespace Cypress {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
interface Chainable<Subject> {
|
interface Chainable<Subject> {
|
||||||
login(email: string, password: string): void;
|
login(email: string, password: string): void;
|
||||||
|
getByTestId(selector: string): Chainable<JQuery<HTMLElement>>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
@ -31,3 +32,6 @@ Cypress.Commands.add('login', (email, password) => {
|
|||||||
//
|
//
|
||||||
// -- This will overwrite an existing command --
|
// -- This will overwrite an existing command --
|
||||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||||
|
Cypress.Commands.add('getByTestId', (selector, ...args) => {
|
||||||
|
return cy.get(`[data-testid=${selector}]`, ...args);
|
||||||
|
});
|
||||||
|
9
apps/explorer-e2e/src/support/pages/assets-page.js
Normal file
9
apps/explorer-e2e/src/support/pages/assets-page.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import BasePage from './base-page';
|
||||||
|
|
||||||
|
export default class AssetsPage extends BasePage {
|
||||||
|
assetHeader = 'asset-header';
|
||||||
|
|
||||||
|
validateAssetsDisplayed() {
|
||||||
|
this.validateBlockDataDisplayed(this.assetHeader);
|
||||||
|
}
|
||||||
|
}
|
79
apps/explorer-e2e/src/support/pages/base-page.js
Normal file
79
apps/explorer-e2e/src/support/pages/base-page.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
export default class BasePage {
|
||||||
|
transactionsUrl = '/txs';
|
||||||
|
blocksUrl = '/blocks';
|
||||||
|
partiesUrl = '/parties';
|
||||||
|
assetsUrl = '/assets';
|
||||||
|
genesisUrl = '/genesis';
|
||||||
|
governanceUrl = '/governance';
|
||||||
|
marketsUrl = '/markets';
|
||||||
|
networkParametersUrl = '/network-parameters';
|
||||||
|
validatorsUrl = '/validators';
|
||||||
|
blockExplorerHeader = 'explorer-header';
|
||||||
|
searchField = 'search-input';
|
||||||
|
|
||||||
|
navigateToTxs() {
|
||||||
|
cy.get(`a[href='${this.transactionsUrl}']`).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToBlocks() {
|
||||||
|
cy.get(`a[href='${this.blocksUrl}']`).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToParties() {
|
||||||
|
cy.get(`a[href='${this.partiesUrl}']`).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToAssets() {
|
||||||
|
cy.get(`a[href*='${this.assetsUrl}']`).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToGenesis() {
|
||||||
|
cy.get(`a[href='${this.genesisUrl}']`).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToGovernance() {
|
||||||
|
cy.get(`a[href='${this.governanceUrl}']`).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToMarkets() {
|
||||||
|
cy.get(`a[href='${this.marketsUrl}']`).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToNetworkParameters() {
|
||||||
|
cy.get(`a[href='${this.networkParametersUrl}']`).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToValidators() {
|
||||||
|
cy.get(`a[href='${this.validatorsUrl}']`).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
search(searchText) {
|
||||||
|
cy.getByTestId(this.searchField).type(searchText);
|
||||||
|
}
|
||||||
|
|
||||||
|
validateSearchDisplayed() {
|
||||||
|
cy.getByTestId(this.blockExplorerHeader).should(
|
||||||
|
'have.text',
|
||||||
|
'Vega Block Explorer'
|
||||||
|
);
|
||||||
|
cy.getByTestId(this.searchField).should('be.visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
validateBlockDataDisplayed(headerTestId) {
|
||||||
|
cy.getByTestId(headerTestId).then(($assetHeaders) => {
|
||||||
|
const headersAmount = parseInt($assetHeaders.length);
|
||||||
|
|
||||||
|
cy.wrap($assetHeaders).each(($header) => {
|
||||||
|
expect($header).to.not.be.empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('.language-json')
|
||||||
|
.each(($asset, index, $list) => {
|
||||||
|
expect($asset).to.not.be.empty;
|
||||||
|
})
|
||||||
|
.then(($list) => {
|
||||||
|
expect($list).to.have.length(headersAmount);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
9
apps/explorer-e2e/src/support/pages/genesis-page.js
Normal file
9
apps/explorer-e2e/src/support/pages/genesis-page.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import BasePage from './base-page';
|
||||||
|
|
||||||
|
export default class GenesisPage extends BasePage {
|
||||||
|
GenesisHeader = 'genesis-header';
|
||||||
|
|
||||||
|
genesisFieldsDisplayed() {
|
||||||
|
this.validateBlockDataDisplayed(this.GenesisHeader);
|
||||||
|
}
|
||||||
|
}
|
3
apps/explorer-e2e/src/support/pages/home-page.js
Normal file
3
apps/explorer-e2e/src/support/pages/home-page.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import BasePage from './base-page';
|
||||||
|
|
||||||
|
export default class HomePage extends BasePage {}
|
9
apps/explorer-e2e/src/support/pages/markets-page.js
Normal file
9
apps/explorer-e2e/src/support/pages/markets-page.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import BasePage from './base-page';
|
||||||
|
|
||||||
|
export default class MarketsPage extends BasePage {
|
||||||
|
marketHeaders = 'markets-header';
|
||||||
|
|
||||||
|
validateMarketDataDisplayed() {
|
||||||
|
this.validateBlockDataDisplayed(this.marketHeaders);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import BasePage from './base-page';
|
||||||
|
|
||||||
|
export default class NetworkParametersPage extends BasePage {
|
||||||
|
networkParametersHeader = 'network-param-header';
|
||||||
|
parameters = 'parameters';
|
||||||
|
|
||||||
|
verifyNetworkParametersDisplayed() {
|
||||||
|
cy.getByTestId(this.networkParametersHeader).should(
|
||||||
|
'have.text',
|
||||||
|
'NetworkParameters'
|
||||||
|
);
|
||||||
|
cy.getByTestId(this.parameters).should('not.be.empty');
|
||||||
|
}
|
||||||
|
}
|
37
apps/explorer-e2e/src/support/pages/transactions-page.js
Normal file
37
apps/explorer-e2e/src/support/pages/transactions-page.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import BasePage from './base-page';
|
||||||
|
|
||||||
|
export default class TransactionsPage extends BasePage {
|
||||||
|
blockRow = 'block-row';
|
||||||
|
blockHeight = 'block-height';
|
||||||
|
numberOfTransactions = 'num-txs';
|
||||||
|
validatorLink = 'validator-link';
|
||||||
|
blockTime = 'block-time';
|
||||||
|
refreshBtn = 'refresh';
|
||||||
|
|
||||||
|
validateTransactionsPagedisplayed() {
|
||||||
|
cy.getByTestId(this.blockRow).should('have.length.above', 1);
|
||||||
|
cy.getByTestId(this.blockHeight).first().should('not.be.empty');
|
||||||
|
cy.getByTestId(this.numberOfTransactions).first().should('not.be.empty');
|
||||||
|
cy.getByTestId(this.validatorLink).first().should('not.be.empty');
|
||||||
|
cy.getByTestId(this.blockTime).first().should('not.be.empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
validateRefreshBtn() {
|
||||||
|
cy.getByTestId(this.blockHeight)
|
||||||
|
.first()
|
||||||
|
.invoke('text')
|
||||||
|
.then((blockHeightTxt) => {
|
||||||
|
cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||||
|
//Wait needed to allow blocks to change
|
||||||
|
cy.getByTestId(this.refreshBtn).click();
|
||||||
|
|
||||||
|
cy.getByTestId(this.blockHeight)
|
||||||
|
.first()
|
||||||
|
.invoke('text')
|
||||||
|
.should((newBlockHeightText) => {
|
||||||
|
expect(blockHeightTxt).not.to.eq(newBlockHeightText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
cy.getByTestId(this.blockTime).first();
|
||||||
|
}
|
||||||
|
}
|
18
apps/explorer-e2e/src/support/pages/validators-page.js
Normal file
18
apps/explorer-e2e/src/support/pages/validators-page.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import BasePage from './base-page';
|
||||||
|
|
||||||
|
export default class ValidatorPage extends BasePage {
|
||||||
|
tendermintHeader = 'tendermint-header';
|
||||||
|
vegaHeader = 'vega-header';
|
||||||
|
tendermintData = 'tendermint-data';
|
||||||
|
vegaData = 'vega-data';
|
||||||
|
|
||||||
|
validateValidatorsDisplayed() {
|
||||||
|
cy.getByTestId(this.tendermintHeader).should(
|
||||||
|
'have.text',
|
||||||
|
'Tendermint data'
|
||||||
|
);
|
||||||
|
cy.getByTestId(this.tendermintData).should('not.be.empty');
|
||||||
|
cy.getByTestId(this.vegaHeader).should('have.text', 'Vega data');
|
||||||
|
cy.getByTestId(this.vegaData).should('not.be.empty');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
|
||||||
|
|
||||||
|
import AssetsPage from '../pages/assets-page';
|
||||||
|
const assetPage = new AssetsPage();
|
||||||
|
|
||||||
|
When('I navigate to the asset page', () => {
|
||||||
|
assetPage.navigateToAssets();
|
||||||
|
});
|
||||||
|
|
||||||
|
Then('asset page is correctly displayed', () => {
|
||||||
|
assetPage.validateAssetsDisplayed();
|
||||||
|
});
|
@ -0,0 +1,5 @@
|
|||||||
|
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
|
||||||
|
|
||||||
|
Given('I am on the homepage', () => {
|
||||||
|
cy.visit('/');
|
||||||
|
});
|
@ -1,5 +1 @@
|
|||||||
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
|
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
|
||||||
|
|
||||||
Given('I go to the homepage', () => {
|
|
||||||
cy.visit('/');
|
|
||||||
});
|
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
|
||||||
|
|
||||||
|
import MarketsPage from '../pages/markets-page';
|
||||||
|
const marketsPage = new MarketsPage();
|
||||||
|
|
||||||
|
When('I navigate to the markets page', () => {
|
||||||
|
marketsPage.navigateToMarkets();
|
||||||
|
});
|
||||||
|
|
||||||
|
Then('markets page is correctly displayed', () => {
|
||||||
|
marketsPage.validateMarketDataDisplayed();
|
||||||
|
});
|
@ -0,0 +1,22 @@
|
|||||||
|
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
|
||||||
|
|
||||||
|
import TransactionsPage from '../pages/transactions-page';
|
||||||
|
const transactionsPage = new TransactionsPage();
|
||||||
|
|
||||||
|
When('I navigate to the transactions page', () => {
|
||||||
|
transactionsPage.navigateToTxs();
|
||||||
|
});
|
||||||
|
|
||||||
|
When('I navigate to the blocks page', () => {
|
||||||
|
transactionsPage.navigateToBlocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
Then('transactions page is correctly displayed', () => {
|
||||||
|
transactionsPage.validateTransactionsPagedisplayed();
|
||||||
|
transactionsPage.validateRefreshBtn();
|
||||||
|
});
|
||||||
|
|
||||||
|
Then('blocks page is correctly displayed', () => {
|
||||||
|
transactionsPage.validateTransactionsPagedisplayed();
|
||||||
|
transactionsPage.validateRefreshBtn();
|
||||||
|
});
|
@ -0,0 +1,12 @@
|
|||||||
|
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
|
||||||
|
|
||||||
|
import ValidatorPage from '../pages/validators-page';
|
||||||
|
const validatorsPage = new ValidatorPage();
|
||||||
|
|
||||||
|
When('I navigate to the validators page', () => {
|
||||||
|
validatorsPage.navigateToValidators();
|
||||||
|
});
|
||||||
|
|
||||||
|
Then('validators page is correctly displayed', () => {
|
||||||
|
validatorsPage.validateValidatorsDisplayed();
|
||||||
|
});
|
@ -20,6 +20,15 @@ NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
|||||||
|
|
||||||
# App configuration variables
|
# App configuration variables
|
||||||
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
|
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
|
||||||
NX_TENDERMINT_URL = "https://mainnet-observer-proxy01.ops.vega.xyz"
|
NX_TENDERMINT_URL = "https://lb.testnet.vega.xyz/tm"
|
||||||
NX_TENDERMINT_WEBSOCKET_URL = "wss://mainnet-observer-proxy01.ops.vega.xyz/websocket"
|
NX_TENDERMINT_WEBSOCKET_URL = "wss://lb.testnet.vega.xyz/tm/websocket"
|
||||||
NX_VEGA_URL = "http://mainnet-observer.ops.vega.xyz:3008/query"
|
NX_VEGA_URL = "https://lb.testnet.vega.xyz/query"
|
||||||
|
|
||||||
|
# App flags
|
||||||
|
NX_EXPLORER_ASSETS = 1
|
||||||
|
NX_EXPLORER_GENESIS = 1
|
||||||
|
NX_EXPLORER_GOVERNANCE = 1
|
||||||
|
NX_EXPLORER_MARKETS = 1
|
||||||
|
NX_EXPLORER_NETWORK_PARAMETERS = 1
|
||||||
|
NX_EXPLORER_PARTIES = 1
|
||||||
|
NX_EXPLORER_VALIDATORS = 1
|
||||||
|
34
apps/explorer/.env.devent
Normal file
34
apps/explorer/.env.devent
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# React Environment Variables
|
||||||
|
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
|
||||||
|
|
||||||
|
# Netlify Environment Variables
|
||||||
|
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
||||||
|
NX_VERSION=$npm_package_version
|
||||||
|
NX_REPOSITORY_URL=$REPOSITORY_URL
|
||||||
|
NX_BRANCH=$BRANCH
|
||||||
|
NX_PULL_REQUEST=$PULL_REQUEST
|
||||||
|
NX_HEAD=$HEAD
|
||||||
|
NX_COMMIT_REF=$COMMIT_REF
|
||||||
|
NX_CONTEXT=$CONTEXT
|
||||||
|
NX_REVIEW_ID=$REVIEW_ID
|
||||||
|
NX_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
|
||||||
|
NX_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
|
||||||
|
NX_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
|
||||||
|
NX_URL=$URL
|
||||||
|
NX_DEPLOY_URL=$DEPLOY_URL
|
||||||
|
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
||||||
|
|
||||||
|
# App configuration variables
|
||||||
|
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
|
||||||
|
NX_TENDERMINT_URL = "https://n04.d.vega.xyz/tm"
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL = "wss://n04.d.vega.xyz/tm/websocket"
|
||||||
|
NX_VEGA_URL = "https://n04.d.vega.xyz/query"
|
||||||
|
|
||||||
|
# App flags
|
||||||
|
NX_EXPLORER_ASSETS = 1
|
||||||
|
NX_EXPLORER_GENESIS = 1
|
||||||
|
NX_EXPLORER_GOVERNANCE = 1
|
||||||
|
NX_EXPLORER_MARKETS = 1
|
||||||
|
NX_EXPLORER_NETWORK_PARAMETERS = 1
|
||||||
|
NX_EXPLORER_PARTIES = 1
|
||||||
|
NX_EXPLORER_VALIDATORS = 1
|
34
apps/explorer/.env.mainnet
Normal file
34
apps/explorer/.env.mainnet
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# React Environment Variables
|
||||||
|
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
|
||||||
|
|
||||||
|
# Netlify Environment Variables
|
||||||
|
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
||||||
|
NX_VERSION=$npm_package_version
|
||||||
|
NX_REPOSITORY_URL=$REPOSITORY_URL
|
||||||
|
NX_BRANCH=$BRANCH
|
||||||
|
NX_PULL_REQUEST=$PULL_REQUEST
|
||||||
|
NX_HEAD=$HEAD
|
||||||
|
NX_COMMIT_REF=$COMMIT_REF
|
||||||
|
NX_CONTEXT=$CONTEXT
|
||||||
|
NX_REVIEW_ID=$REVIEW_ID
|
||||||
|
NX_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
|
||||||
|
NX_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
|
||||||
|
NX_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
|
||||||
|
NX_URL=$URL
|
||||||
|
NX_DEPLOY_URL=$DEPLOY_URL
|
||||||
|
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
||||||
|
|
||||||
|
# App configuration variables
|
||||||
|
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
|
||||||
|
NX_TENDERMINT_URL = "https://mainnet-observer-proxy01.ops.vega.xyz/"
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL = "wss://mainnet-observer-proxy01.ops.vega.xyz/websocket"
|
||||||
|
NX_VEGA_URL = "https://api.token.vega.xyz/query"
|
||||||
|
|
||||||
|
# App flags
|
||||||
|
NX_EXPLORER_ASSETS = 1
|
||||||
|
NX_EXPLORER_GENESIS = 1
|
||||||
|
NX_EXPLORER_GOVERNANCE = 1
|
||||||
|
NX_EXPLORER_MARKETS = 1
|
||||||
|
NX_EXPLORER_NETWORK_PARAMETERS = 1
|
||||||
|
NX_EXPLORER_PARTIES = 1
|
||||||
|
NX_EXPLORER_VALIDATORS = 1
|
35
apps/explorer/.env.stagnet1
Normal file
35
apps/explorer/.env.stagnet1
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# React Environment Variables
|
||||||
|
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
|
||||||
|
|
||||||
|
# Netlify Environment Variables
|
||||||
|
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
||||||
|
NX_VERSION=$npm_package_version
|
||||||
|
NX_REPOSITORY_URL=$REPOSITORY_URL
|
||||||
|
NX_BRANCH=$BRANCH
|
||||||
|
NX_PULL_REQUEST=$PULL_REQUEST
|
||||||
|
NX_HEAD=$HEAD
|
||||||
|
NX_COMMIT_REF=$COMMIT_REF
|
||||||
|
NX_CONTEXT=$CONTEXT
|
||||||
|
NX_REVIEW_ID=$REVIEW_ID
|
||||||
|
NX_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
|
||||||
|
NX_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
|
||||||
|
NX_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
|
||||||
|
NX_URL=$URL
|
||||||
|
NX_DEPLOY_URL=$DEPLOY_URL
|
||||||
|
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
||||||
|
|
||||||
|
# App configuration variables
|
||||||
|
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
|
||||||
|
NX_TENDERMINT_URL = "https://n03.s.vega.xyz/tm"
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL = "wss://n03.s.vega.xyz/tm/websocket"
|
||||||
|
NX_VEGA_URL = "https://n03.s.vega.xyz/query"
|
||||||
|
|
||||||
|
|
||||||
|
# App flags
|
||||||
|
NX_EXPLORER_ASSETS = 1
|
||||||
|
NX_EXPLORER_GENESIS = 1
|
||||||
|
NX_EXPLORER_GOVERNANCE = 1
|
||||||
|
NX_EXPLORER_MARKETS = 1
|
||||||
|
NX_EXPLORER_NETWORK_PARAMETERS = 1
|
||||||
|
NX_EXPLORER_PARTIES = 1
|
||||||
|
NX_EXPLORER_VALIDATORS = 1
|
34
apps/explorer/.env.stagnet2
Normal file
34
apps/explorer/.env.stagnet2
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# React Environment Variables
|
||||||
|
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
|
||||||
|
|
||||||
|
# Netlify Environment Variables
|
||||||
|
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
||||||
|
NX_VERSION=$npm_package_version
|
||||||
|
NX_REPOSITORY_URL=$REPOSITORY_URL
|
||||||
|
NX_BRANCH=$BRANCH
|
||||||
|
NX_PULL_REQUEST=$PULL_REQUEST
|
||||||
|
NX_HEAD=$HEAD
|
||||||
|
NX_COMMIT_REF=$COMMIT_REF
|
||||||
|
NX_CONTEXT=$CONTEXT
|
||||||
|
NX_REVIEW_ID=$REVIEW_ID
|
||||||
|
NX_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
|
||||||
|
NX_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
|
||||||
|
NX_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
|
||||||
|
NX_URL=$URL
|
||||||
|
NX_DEPLOY_URL=$DEPLOY_URL
|
||||||
|
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
||||||
|
|
||||||
|
# App configuration variables
|
||||||
|
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
|
||||||
|
NX_TENDERMINT_URL = "https://n03.stagnet2.vega.xyz/tm"
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL = "wss://n03.stagnet2.vega.xyz/tm/websocket"
|
||||||
|
NX_VEGA_URL = "https://n03.stagnet2.vega.xyz/query"
|
||||||
|
|
||||||
|
# App flags
|
||||||
|
NX_EXPLORER_ASSETS = 1
|
||||||
|
NX_EXPLORER_GENESIS = 1
|
||||||
|
NX_EXPLORER_GOVERNANCE = 1
|
||||||
|
NX_EXPLORER_MARKETS = 1
|
||||||
|
NX_EXPLORER_NETWORK_PARAMETERS = 1
|
||||||
|
NX_EXPLORER_PARTIES = 1
|
||||||
|
NX_EXPLORER_VALIDATORS = 1
|
34
apps/explorer/.env.testnet
Normal file
34
apps/explorer/.env.testnet
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# React Environment Variables
|
||||||
|
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
|
||||||
|
|
||||||
|
# Netlify Environment Variables
|
||||||
|
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
||||||
|
NX_VERSION=$npm_package_version
|
||||||
|
NX_REPOSITORY_URL=$REPOSITORY_URL
|
||||||
|
NX_BRANCH=$BRANCH
|
||||||
|
NX_PULL_REQUEST=$PULL_REQUEST
|
||||||
|
NX_HEAD=$HEAD
|
||||||
|
NX_COMMIT_REF=$COMMIT_REF
|
||||||
|
NX_CONTEXT=$CONTEXT
|
||||||
|
NX_REVIEW_ID=$REVIEW_ID
|
||||||
|
NX_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
|
||||||
|
NX_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
|
||||||
|
NX_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
|
||||||
|
NX_URL=$URL
|
||||||
|
NX_DEPLOY_URL=$DEPLOY_URL
|
||||||
|
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
||||||
|
|
||||||
|
# App configuration variables
|
||||||
|
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
|
||||||
|
NX_TENDERMINT_URL = "https://lb.testnet.vega.xyz/tm"
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL = "wss://lb.testnet.vega.xyz/tm/websocket"
|
||||||
|
NX_VEGA_URL = "https://lb.testnet.vega.xyz/query"
|
||||||
|
|
||||||
|
# App flags
|
||||||
|
NX_EXPLORER_ASSETS = 1
|
||||||
|
NX_EXPLORER_GENESIS = 1
|
||||||
|
NX_EXPLORER_GOVERNANCE = 1
|
||||||
|
NX_EXPLORER_MARKETS = 1
|
||||||
|
NX_EXPLORER_NETWORK_PARAMETERS = 1
|
||||||
|
NX_EXPLORER_PARTIES = 1
|
||||||
|
NX_EXPLORER_VALIDATORS = 1
|
@ -74,6 +74,19 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"build-netlify": {
|
||||||
|
"builder": "@nrwl/workspace:run-commands",
|
||||||
|
"options": {
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": "cp apps/explorer/netlify.toml netlify.toml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "nx build explorer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tags": []
|
"tags": []
|
||||||
|
@ -4,7 +4,11 @@ interface BlocksRefetchProps {
|
|||||||
|
|
||||||
export const BlocksRefetch = ({ refetch }: BlocksRefetchProps) => {
|
export const BlocksRefetch = ({ refetch }: BlocksRefetchProps) => {
|
||||||
return (
|
return (
|
||||||
<button onClick={() => refetch()} className="underline mb-28">
|
<button
|
||||||
|
onClick={() => refetch()}
|
||||||
|
className="underline mb-28"
|
||||||
|
data-testid="refresh"
|
||||||
|
>
|
||||||
Refresh to see latest blocks
|
Refresh to see latest blocks
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
@ -3,7 +3,7 @@ import { TendermintBlockchainResponse } from '../../../routes/blocks/tendermint-
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { SecondsAgo } from '../../seconds-ago';
|
import { SecondsAgo } from '../../seconds-ago';
|
||||||
import { TxsPerBlock } from '../../txs/txs-per-block';
|
import { TxsPerBlock } from '../../txs/txs-per-block';
|
||||||
import { Table } from '../../table';
|
import { Table, TableRow, TableCell } from '../../table';
|
||||||
|
|
||||||
interface BlocksProps {
|
interface BlocksProps {
|
||||||
data: TendermintBlockchainResponse | undefined;
|
data: TendermintBlockchainResponse | undefined;
|
||||||
@ -20,33 +20,39 @@ export const BlocksTable = ({ data, showTransactions }: BlocksProps) => {
|
|||||||
{data.result?.block_metas?.map((block, index) => {
|
{data.result?.block_metas?.map((block, index) => {
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={index}>
|
<React.Fragment key={index}>
|
||||||
<tr className="bg-neutral-850 border-b-4 border-b-black">
|
<TableRow dataTestId="block-row" modifier="background">
|
||||||
<td className="pl-4 py-2">
|
<TableCell dataTestId="block-height" className="pl-4 py-2">
|
||||||
<Link
|
<Link
|
||||||
to={`/blocks/${block.header?.height}`}
|
to={`/blocks/${block.header?.height}`}
|
||||||
className="text-vega-yellow"
|
className="text-vega-yellow"
|
||||||
>
|
>
|
||||||
{block.header?.height}
|
{block.header?.height}
|
||||||
</Link>
|
</Link>
|
||||||
</td>
|
</TableCell>
|
||||||
<td className="px-8 text-center">
|
<TableCell dataTestId="num-txs" className="px-8 text-center">
|
||||||
{block.num_txs === '1'
|
{block.num_txs === '1'
|
||||||
? '1 transaction'
|
? '1 transaction'
|
||||||
: `${block.num_txs} transactions`}
|
: `${block.num_txs} transactions`}
|
||||||
</td>
|
</TableCell>
|
||||||
<td className="px-8 text-center">
|
<TableCell
|
||||||
|
dataTestId="validator-link"
|
||||||
|
className="px-8 text-center"
|
||||||
|
>
|
||||||
<Link to={`/validators/${block.header?.proposer_address}`}>
|
<Link to={`/validators/${block.header?.proposer_address}`}>
|
||||||
{block.header.proposer_address}
|
{block.header.proposer_address}
|
||||||
</Link>
|
</Link>
|
||||||
</td>
|
</TableCell>
|
||||||
<td className="text-center pr-28 text-neutral-300">
|
<TableCell
|
||||||
|
dataTestId="block-time"
|
||||||
|
className="text-center pr-28 text-neutral-300"
|
||||||
|
>
|
||||||
<SecondsAgo date={block.header?.time} />
|
<SecondsAgo date={block.header?.time} />
|
||||||
</td>
|
</TableCell>
|
||||||
</tr>
|
</TableRow>
|
||||||
{showTransactions && (
|
{showTransactions && (
|
||||||
<tr>
|
<TableRow>
|
||||||
<TxsPerBlock blockHeight={block.header?.height} />
|
<TxsPerBlock blockHeight={block.header?.height} />
|
||||||
</tr>
|
</TableRow>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
@ -28,12 +28,16 @@ export const JumpToBlock = () => {
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="block-input"
|
id="block-input"
|
||||||
type='tel'
|
type="tel"
|
||||||
name={'blockNumber'}
|
name={'blockNumber'}
|
||||||
placeholder={'Block number'}
|
placeholder={'Block number'}
|
||||||
className="form-input"
|
className="bg-white-25 border-white border px-8 py-4 placeholder-white-60"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
className="border-white border px-28 py-4 cursor-pointer"
|
||||||
|
type={'submit'}
|
||||||
|
value={'Go'}
|
||||||
/>
|
/>
|
||||||
<input className="form-submit" type={'submit'} value={'Go'} />
|
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
19
apps/explorer/src/app/components/route-title/index.tsx
Normal file
19
apps/explorer/src/app/components/route-title/index.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import classnames from 'classnames';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface RouteTitleProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RouteTitle = ({ children, className }: RouteTitleProps) => {
|
||||||
|
const classes = classnames(
|
||||||
|
'font-alpha',
|
||||||
|
'text-h3',
|
||||||
|
'uppercase',
|
||||||
|
'mt-12',
|
||||||
|
'mb-28',
|
||||||
|
className
|
||||||
|
);
|
||||||
|
return <h1 className={classes}>{children}</h1>;
|
||||||
|
};
|
@ -91,10 +91,15 @@ const Search = () => {
|
|||||||
const { search, onChange } = useGuess();
|
const { search, onChange } = useGuess();
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<h1>Vega Block Explorer</h1>
|
<h1 data-testid="explorer-header">Vega Block Explorer</h1>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label htmlFor="search">Search: </label>
|
<label htmlFor="search">Search: </label>
|
||||||
<input name="search" value={search} onChange={(e) => onChange(e)} />
|
<input
|
||||||
|
data-testid="search-field"
|
||||||
|
name="search"
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => onChange(e)}
|
||||||
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
@ -1 +1,76 @@
|
|||||||
export { Table } from './table';
|
import React, { ThHTMLAttributes } from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
interface TableProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TableHeaderProps extends ThHTMLAttributes<HTMLTableRowElement> {
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TableChildProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
dataTestId?: string;
|
||||||
|
modifier?: 'bordered' | 'background';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Table = ({ children }: TableProps) => {
|
||||||
|
return (
|
||||||
|
<div className="overflow-x-auto whitespace-nowrap mb-28">
|
||||||
|
<table className="w-full">
|
||||||
|
<tbody>{children}</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TableRow = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
dataTestId,
|
||||||
|
modifier,
|
||||||
|
}: TableChildProps) => {
|
||||||
|
const cellClasses = classnames(className, {
|
||||||
|
'border-b border-white-40': modifier === 'bordered',
|
||||||
|
'bg-white-25 border-b-4 border-b-black': modifier === 'background',
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<tr className={cellClasses} data-testid={dataTestId || null}>
|
||||||
|
{children}
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TableHeader = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: TableHeaderProps) => {
|
||||||
|
const cellClasses = classnames(className, {
|
||||||
|
'text-left, font-normal': props?.scope === 'row',
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<tr className={cellClasses} {...props}>
|
||||||
|
{children}
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TableCell = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
dataTestId,
|
||||||
|
modifier,
|
||||||
|
}: TableChildProps) => {
|
||||||
|
const cellClasses = classnames(className, {
|
||||||
|
'py-4': modifier === 'bordered',
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<td className={cellClasses} data-testid={dataTestId || null}>
|
||||||
|
{children}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
interface TableProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Table = ({ children }: TableProps) => {
|
|
||||||
return (
|
|
||||||
<div className="overflow-x-auto whitespace-nowrap mb-28">
|
|
||||||
<table className="w-full">
|
|
||||||
<tbody>{children}</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -5,7 +5,6 @@ const ELLIPSIS = '\u2026';
|
|||||||
interface TruncateInlineProps {
|
interface TruncateInlineProps {
|
||||||
text: string | null;
|
text: string | null;
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: React.CSSProperties;
|
|
||||||
children?: (truncatedText: string) => React.ReactElement;
|
children?: (truncatedText: string) => React.ReactElement;
|
||||||
startChars?: number; // number chars to show before ellipsis
|
startChars?: number; // number chars to show before ellipsis
|
||||||
endChars?: number; // number of chars to show after ellipsis
|
endChars?: number; // number of chars to show after ellipsis
|
||||||
@ -20,7 +19,6 @@ interface TruncateInlineProps {
|
|||||||
export function TruncateInline({
|
export function TruncateInline({
|
||||||
text,
|
text,
|
||||||
className,
|
className,
|
||||||
style,
|
|
||||||
children,
|
children,
|
||||||
startChars,
|
startChars,
|
||||||
endChars,
|
endChars,
|
||||||
@ -32,7 +30,6 @@ export function TruncateInline({
|
|||||||
|
|
||||||
const wrapperProps = {
|
const wrapperProps = {
|
||||||
title: text,
|
title: text,
|
||||||
style,
|
|
||||||
className,
|
className,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Routes } from '../../../routes/router-config';
|
import { Routes } from '../../../routes/router-config';
|
||||||
import { Result } from '../../../routes/txs/tendermint-transaction-response.d';
|
import { Result } from '../../../routes/txs/tendermint-transaction-response.d';
|
||||||
import { Table } from '../../table';
|
import { Table, TableRow, TableCell } from '../../table';
|
||||||
|
|
||||||
interface TxDetailsProps {
|
interface TxDetailsProps {
|
||||||
txData: Result | undefined;
|
txData: Result | undefined;
|
||||||
@ -15,35 +15,35 @@ export const TxDetails = ({ txData, pubKey }: TxDetailsProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Table>
|
<Table>
|
||||||
<tr>
|
<TableRow>
|
||||||
<td>Hash</td>
|
<TableCell>Hash</TableCell>
|
||||||
<td>{txData.hash}</td>
|
<TableCell dataTestId="hash">{txData.hash}</TableCell>
|
||||||
</tr>
|
</TableRow>
|
||||||
{pubKey ? (
|
{pubKey ? (
|
||||||
<tr>
|
<TableRow>
|
||||||
<td>Submitted by</td>
|
<td>Submitted by</td>
|
||||||
<td>
|
<td data-testid="submitted-by">
|
||||||
<Link to={`/${Routes.PARTIES}/${pubKey}`}>{pubKey}</Link>
|
<Link to={`/${Routes.PARTIES}/${pubKey}`}>{pubKey}</Link>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</TableRow>
|
||||||
) : (
|
) : (
|
||||||
<tr>
|
<TableRow>
|
||||||
<td>Submitted by</td>
|
<td>Submitted by</td>
|
||||||
<td>Awaiting decoded transaction data</td>
|
<td>Awaiting decoded transaction data</td>
|
||||||
</tr>
|
</TableRow>
|
||||||
)}
|
)}
|
||||||
{txData.height ? (
|
{txData.height ? (
|
||||||
<tr>
|
<TableRow>
|
||||||
<td>Block</td>
|
<td>Block</td>
|
||||||
<td>
|
<td data-testid="block">
|
||||||
<Link to={`/blocks/${txData.height}`}>{txData.height}</Link>
|
<Link to={`/blocks/${txData.height}`}>{txData.height}</Link>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</TableRow>
|
||||||
) : null}
|
) : null}
|
||||||
<tr>
|
<TableRow>
|
||||||
<td>Encoded tnx</td>
|
<td>Encoded tnx</td>
|
||||||
<td>{txData.tx}</td>
|
<td data-testid="encoded-tnx">{txData.tx}</td>
|
||||||
</tr>
|
</TableRow>
|
||||||
</Table>
|
</Table>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
import React from 'react';
|
||||||
import { WebSocketHook } from "react-use-websocket/dist/lib/types";
|
import { WebSocketHook } from 'react-use-websocket/dist/lib/types';
|
||||||
|
|
||||||
export type WebsocketContextShape = WebSocketHook;
|
export type WebsocketContextShape = WebSocketHook;
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ export const TendermintWebsocketContext =
|
|||||||
export function useTendermintWebsocketContext() {
|
export function useTendermintWebsocketContext() {
|
||||||
const context = React.useContext(TendermintWebsocketContext);
|
const context = React.useContext(TendermintWebsocketContext);
|
||||||
if (context === null) {
|
if (context === null) {
|
||||||
throw new Error("useWebsocket must be used within WebsocketContext");
|
throw new Error('useWebsocket must be used within WebsocketContext');
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
13
apps/explorer/src/app/lib/flags.ts
Normal file
13
apps/explorer/src/app/lib/flags.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const truthy = ['1', 'true'];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
assets: truthy.includes(process.env['NX_EXPLORER_ASSETS'] || ''),
|
||||||
|
genesis: truthy.includes(process.env['NX_EXPLORER_GENESIS'] || ''),
|
||||||
|
governance: truthy.includes(process.env['NX_EXPLORER_GOVERNANCE'] || ''),
|
||||||
|
markets: truthy.includes(process.env['NX_EXPLORER_MARKETS'] || ''),
|
||||||
|
networkParameters: truthy.includes(
|
||||||
|
process.env['NX_EXPLORER_NETWORK_PARAMETERS'] || ''
|
||||||
|
),
|
||||||
|
parties: truthy.includes(process.env['NX_EXPLORER_PARTIES'] || ''),
|
||||||
|
validators: truthy.includes(process.env['NX_EXPLORER_VALIDATORS'] || ''),
|
||||||
|
};
|
@ -39,7 +39,7 @@ const Assets = () => {
|
|||||||
<h1>Assets</h1>
|
<h1>Assets</h1>
|
||||||
{data?.assets.map((a) => (
|
{data?.assets.map((a) => (
|
||||||
<React.Fragment key={a.id}>
|
<React.Fragment key={a.id}>
|
||||||
<h2>
|
<h2 data-testid="asset-header">
|
||||||
{a.name} ({a.symbol})
|
{a.name} ({a.symbol})
|
||||||
</h2>
|
</h2>
|
||||||
<SyntaxHighlighter data={a} />
|
<SyntaxHighlighter data={a} />
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { DATA_SOURCES } from '../../../config';
|
import { DATA_SOURCES } from '../../../config';
|
||||||
import useFetch from '../../../hooks/use-fetch';
|
import useFetch from '../../../hooks/use-fetch';
|
||||||
import { TendermintBlockchainResponse } from '../tendermint-blockchain-response';
|
import { TendermintBlockchainResponse } from '../tendermint-blockchain-response';
|
||||||
|
import { RouteTitle } from '../../../components/route-title';
|
||||||
import { BlocksTable, BlocksRefetch } from '../../../components/blocks';
|
import { BlocksTable, BlocksRefetch } from '../../../components/blocks';
|
||||||
import { JumpToBlock } from '../../../components/jump-to-block';
|
import { JumpToBlock } from '../../../components/jump-to-block';
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ const Blocks = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section>
|
<section>
|
||||||
<h1 className="route-header">Blocks</h1>
|
<RouteTitle>Blocks</RouteTitle>
|
||||||
<BlocksRefetch refetch={refetch} />
|
<BlocksRefetch refetch={refetch} />
|
||||||
<BlocksTable data={data} />
|
<BlocksTable data={data} />
|
||||||
</section>
|
</section>
|
||||||
|
@ -3,9 +3,15 @@ import { Link, useParams } from 'react-router-dom';
|
|||||||
import { DATA_SOURCES } from '../../../config';
|
import { DATA_SOURCES } from '../../../config';
|
||||||
import useFetch from '../../../hooks/use-fetch';
|
import useFetch from '../../../hooks/use-fetch';
|
||||||
import { TendermintBlocksResponse } from '../tendermint-blocks-response';
|
import { TendermintBlocksResponse } from '../tendermint-blocks-response';
|
||||||
|
import { RouteTitle } from '../../../components/route-title';
|
||||||
import { TxsPerBlock } from '../../../components/txs/txs-per-block';
|
import { TxsPerBlock } from '../../../components/txs/txs-per-block';
|
||||||
import { SecondsAgo } from '../../../components/seconds-ago';
|
import { SecondsAgo } from '../../../components/seconds-ago';
|
||||||
import { Table } from '../../../components/table';
|
import {
|
||||||
|
Table,
|
||||||
|
TableRow,
|
||||||
|
TableHeader,
|
||||||
|
TableCell,
|
||||||
|
} from '../../../components/table';
|
||||||
|
|
||||||
const Block = () => {
|
const Block = () => {
|
||||||
const { block } = useParams<{ block: string }>();
|
const { block } = useParams<{ block: string }>();
|
||||||
@ -23,25 +29,25 @@ const Block = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<h1 className="route-header">BLOCK {block}</h1>
|
<RouteTitle>BLOCK {block}</RouteTitle>
|
||||||
<Table>
|
<Table>
|
||||||
<tr className="table-bordered-tr">
|
<TableRow modifier="bordered">
|
||||||
<td className="table-bordered-td">Mined by</td>
|
<TableHeader scope="row">Mined by</TableHeader>
|
||||||
<td className="table-bordered-td">
|
<TableCell modifier="bordered">
|
||||||
<Link
|
<Link
|
||||||
className="text-vega-yellow"
|
className="text-vega-yellow"
|
||||||
to={`/validators/${header.proposer_address}`}
|
to={`/validators/${header.proposer_address}`}
|
||||||
>
|
>
|
||||||
{header.proposer_address}
|
{header.proposer_address}
|
||||||
</Link>
|
</Link>
|
||||||
</td>
|
</TableCell>
|
||||||
</tr>
|
</TableRow>
|
||||||
<tr className="table-bordered-tr">
|
<TableRow modifier="bordered">
|
||||||
<td className="table-bordered-td">Time</td>
|
<TableHeader scope="row">Time</TableHeader>
|
||||||
<td className="table-bordered-td">
|
<TableCell modifier="bordered">
|
||||||
<SecondsAgo date={header.time} />
|
<SecondsAgo date={header.time} />
|
||||||
</td>
|
</TableCell>
|
||||||
</tr>
|
</TableRow>
|
||||||
</Table>
|
</Table>
|
||||||
{blockData?.result.block.data.txs.length > 0 && (
|
{blockData?.result.block.data.txs.length > 0 && (
|
||||||
<TxsPerBlock blockHeight={block} />
|
<TxsPerBlock blockHeight={block} />
|
||||||
|
@ -12,7 +12,7 @@ const Genesis = () => {
|
|||||||
if (!genesis?.result.genesis) return null;
|
if (!genesis?.result.genesis) return null;
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<h1>Genesis</h1>
|
<h1 data-testid="genesis-header">Genesis</h1>
|
||||||
<SyntaxHighlighter data={genesis?.result.genesis} />
|
<SyntaxHighlighter data={genesis?.result.genesis} />
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,24 @@
|
|||||||
import { gql, useQuery } from '@apollo/client';
|
import { gql, useQuery } from '@apollo/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { SyntaxHighlighter } from '../../components/syntax-highlighter';
|
import { SyntaxHighlighter } from '../../components/syntax-highlighter';
|
||||||
import { ProposalsQuery } from './__generated__/ProposalsQuery';
|
import {
|
||||||
|
ProposalsQuery,
|
||||||
|
ProposalsQuery_proposals_terms_change,
|
||||||
|
} from './__generated__/ProposalsQuery';
|
||||||
|
|
||||||
|
export function getProposalName(change: ProposalsQuery_proposals_terms_change) {
|
||||||
|
if (change.__typename === 'NewAsset') {
|
||||||
|
return `New asset: ${change.symbol}`;
|
||||||
|
} else if (change.__typename === 'NewMarket') {
|
||||||
|
return `New market: ${change.instrument.name}`;
|
||||||
|
} else if (change.__typename === 'UpdateMarket') {
|
||||||
|
return `Update market: ${change.marketId}`;
|
||||||
|
} else if (change.__typename === 'UpdateNetworkParameter') {
|
||||||
|
return `Update network: ${change.networkParameter.key}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Unknown proposal';
|
||||||
|
}
|
||||||
|
|
||||||
const PROPOSAL_QUERY = gql`
|
const PROPOSAL_QUERY = gql`
|
||||||
query ProposalsQuery {
|
query ProposalsQuery {
|
||||||
@ -90,7 +107,7 @@ const Governance = () => {
|
|||||||
{data.proposals.map((p) => (
|
{data.proposals.map((p) => (
|
||||||
<React.Fragment key={p.id}>
|
<React.Fragment key={p.id}>
|
||||||
{/* TODO get proposal name generator from console */}
|
{/* TODO get proposal name generator from console */}
|
||||||
<h2>{p.id}</h2>
|
<h2>{getProposalName(p.terms.change)}</h2>
|
||||||
<SyntaxHighlighter data={p} />
|
<SyntaxHighlighter data={p} />
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
|
@ -153,7 +153,7 @@ const Markets = () => {
|
|||||||
<h1>Markets</h1>
|
<h1>Markets</h1>
|
||||||
{data.markets.map((m) => (
|
{data.markets.map((m) => (
|
||||||
<React.Fragment key={m.id}>
|
<React.Fragment key={m.id}>
|
||||||
<h2>{m.name}</h2>
|
<h2 data-testid="markets-header">{m.name}</h2>
|
||||||
<SyntaxHighlighter data={m} />
|
<SyntaxHighlighter data={m} />
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { gql, useQuery } from "@apollo/client";
|
import { gql, useQuery } from '@apollo/client';
|
||||||
import { NetworkParametersQuery } from "./__generated__/NetworkParametersQuery";
|
import { NetworkParametersQuery } from './__generated__/NetworkParametersQuery';
|
||||||
|
|
||||||
export const NETWORK_PARAMETERS_QUERY = gql`
|
export const NETWORK_PARAMETERS_QUERY = gql`
|
||||||
query NetworkParametersQuery {
|
query NetworkParametersQuery {
|
||||||
@ -14,8 +14,8 @@ const NetworkParameters = () => {
|
|||||||
const { data } = useQuery<NetworkParametersQuery>(NETWORK_PARAMETERS_QUERY);
|
const { data } = useQuery<NetworkParametersQuery>(NETWORK_PARAMETERS_QUERY);
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<h1>NetworkParameters</h1>
|
<h1 data-testid="network-param-header">NetworkParameters</h1>
|
||||||
<pre>{JSON.stringify(data, null, " ")}</pre>
|
<pre data-testid="parameters">{JSON.stringify(data, null, ' ')}</pre>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,7 @@ import { Blocks } from './blocks/home';
|
|||||||
import { Tx } from './txs/id';
|
import { Tx } from './txs/id';
|
||||||
import { Txs as TxHome } from './txs/home';
|
import { Txs as TxHome } from './txs/home';
|
||||||
import { PendingTxs } from './pending';
|
import { PendingTxs } from './pending';
|
||||||
|
import flags from '../lib/flags';
|
||||||
export const Routes = {
|
export const Routes = {
|
||||||
HOME: '/',
|
HOME: '/',
|
||||||
TX: 'txs',
|
TX: 'txs',
|
||||||
@ -29,6 +29,85 @@ export const Routes = {
|
|||||||
NETWORK_PARAMETERS: 'network-parameters',
|
NETWORK_PARAMETERS: 'network-parameters',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const partiesRoutes = flags.parties
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
path: Routes.PARTIES,
|
||||||
|
name: 'Parties',
|
||||||
|
element: <Party />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
index: true,
|
||||||
|
element: <Parties />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':party',
|
||||||
|
element: <PartySingle />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const assetsRoutes = flags.assets
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
path: Routes.ASSETS,
|
||||||
|
name: 'Assets',
|
||||||
|
element: <Assets />,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const genesisRoutes = flags.genesis
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
path: Routes.GENESIS,
|
||||||
|
name: 'Genesis',
|
||||||
|
element: <Genesis />,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const governanceRoutes = flags.governance
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
path: Routes.GOVERNANCE,
|
||||||
|
name: 'Governance',
|
||||||
|
element: <Governance />,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const marketsRoutes = flags.markets
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
path: Routes.MARKETS,
|
||||||
|
name: 'Markets',
|
||||||
|
element: <Markets />,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const networkParametersRoutes = flags.networkParameters
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
path: Routes.NETWORK_PARAMETERS,
|
||||||
|
name: 'NetworkParameters',
|
||||||
|
element: <NetworkParameters />,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
const validators = flags.validators
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
path: Routes.VALIDATORS,
|
||||||
|
name: 'Validators',
|
||||||
|
element: <Validators />,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
const routerConfig = [
|
const routerConfig = [
|
||||||
{
|
{
|
||||||
path: Routes.HOME,
|
path: Routes.HOME,
|
||||||
@ -70,51 +149,13 @@ const routerConfig = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
...partiesRoutes,
|
||||||
path: Routes.PARTIES,
|
...assetsRoutes,
|
||||||
name: 'Parties',
|
...genesisRoutes,
|
||||||
element: <Party />,
|
...governanceRoutes,
|
||||||
children: [
|
...marketsRoutes,
|
||||||
{
|
...networkParametersRoutes,
|
||||||
index: true,
|
...validators,
|
||||||
element: <Parties />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ':party',
|
|
||||||
element: <PartySingle />,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: Routes.ASSETS,
|
|
||||||
name: 'Assets',
|
|
||||||
element: <Assets />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: Routes.GENESIS,
|
|
||||||
name: 'Genesis',
|
|
||||||
element: <Genesis />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: Routes.GOVERNANCE,
|
|
||||||
name: 'Governance',
|
|
||||||
element: <Governance />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: Routes.MARKETS,
|
|
||||||
name: 'Markets',
|
|
||||||
element: <Markets />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: Routes.NETWORK_PARAMETERS,
|
|
||||||
name: 'NetworkParameters',
|
|
||||||
element: <NetworkParameters />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: Routes.VALIDATORS,
|
|
||||||
name: 'Validators',
|
|
||||||
element: <Validators />,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export default routerConfig;
|
export default routerConfig;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import useFetch from '../../../hooks/use-fetch';
|
import useFetch from '../../../hooks/use-fetch';
|
||||||
import { TendermintBlockchainResponse } from '../../blocks/tendermint-blockchain-response';
|
import { TendermintBlockchainResponse } from '../../blocks/tendermint-blockchain-response';
|
||||||
import { DATA_SOURCES } from '../../../config';
|
import { DATA_SOURCES } from '../../../config';
|
||||||
|
import { RouteTitle } from '../../../components/route-title';
|
||||||
import { BlocksTable, BlocksRefetch } from '../../../components/blocks';
|
import { BlocksTable, BlocksRefetch } from '../../../components/blocks';
|
||||||
import { JumpToBlock } from '../../../components/jump-to-block';
|
import { JumpToBlock } from '../../../components/jump-to-block';
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ const Txs = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section>
|
<section>
|
||||||
<h1>Transactions</h1>
|
<RouteTitle>Transactions</RouteTitle>
|
||||||
<BlocksRefetch refetch={refetch} />
|
<BlocksRefetch refetch={refetch} />
|
||||||
<BlocksTable data={data} showTransactions={true} />
|
<BlocksTable data={data} showTransactions={true} />
|
||||||
</section>
|
</section>
|
||||||
|
@ -44,10 +44,12 @@ const Validators = () => {
|
|||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<h1>Validators</h1>
|
<h1>Validators</h1>
|
||||||
<h2>Tendermint data</h2>
|
<h2 data-testid="tendermint-header">Tendermint data</h2>
|
||||||
<pre>{JSON.stringify(validators, null, ' ')}</pre>
|
<pre data-testid="tendermint-data">
|
||||||
<h2>Vega data</h2>
|
{JSON.stringify(validators, null, ' ')}
|
||||||
<pre>{JSON.stringify(data, null, ' ')}</pre>
|
</pre>
|
||||||
|
<h2 data-testid="vega-header">Vega data</h2>
|
||||||
|
<pre data-testid="vega-data">{JSON.stringify(data, null, ' ')}</pre>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import * as Sentry from '@sentry/react';
|
||||||
|
import { BrowserTracing } from '@sentry/tracing';
|
||||||
import { StrictMode } from 'react';
|
import { StrictMode } from 'react';
|
||||||
import * as ReactDOM from 'react-dom';
|
import * as ReactDOM from 'react-dom';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
@ -5,6 +7,18 @@ import './styles.css';
|
|||||||
|
|
||||||
import App from './app/app';
|
import App from './app/app';
|
||||||
|
|
||||||
|
const dsn = process.env['NX_SENTRY_DSN'];
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (dsn) {
|
||||||
|
Sentry.init({
|
||||||
|
dsn,
|
||||||
|
integrations: [new BrowserTracing()],
|
||||||
|
tracesSampleRate: 0.1,
|
||||||
|
environment: process.env['NODE_ENV'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
|
@ -1,26 +1,4 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
/* You can add global styles to this file, and also import other style files */
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
|
|
||||||
.route-header {
|
|
||||||
@apply font-alpha text-h3 uppercase mt-12 mb-28;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-bordered-tr {
|
|
||||||
@apply border-b border-neutral-500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-bordered-td {
|
|
||||||
@apply py-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Used for text, tel input */
|
|
||||||
.form-input {
|
|
||||||
@apply bg-neutral-800 border-white border px-8 py-4 placeholder-neutral-300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-submit {
|
|
||||||
@apply border-white border px-28 py-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"next",
|
"next",
|
||||||
"next/core-web-vitals"
|
"next/core-web-vitals"
|
||||||
],
|
],
|
||||||
"ignorePatterns": ["!**/*"],
|
"ignorePatterns": ["!**/*", "__generated__"],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
export function Vega({ className }: { className?: string }) {
|
export function Vega({ className }: { className?: string }) {
|
||||||
return (
|
return (
|
||||||
<svg className="w-[76px] h-[16px]" viewBox="0 0 283.5 61.3">
|
<svg
|
||||||
<path d="M26.6,53.1L44,0h8.8L31.2,61.3h-9.5L0,0h8.9L26.6,53.1z" />
|
width="86"
|
||||||
<path d="M89.6,33.3v21.1h34.3v6.9H81.3V0h41.7V7H89.6v19.3h29.8v6.9H89.6z" />
|
height="19"
|
||||||
<path
|
viewBox="0 0 86 19"
|
||||||
d="M156.8,7.5h7.4V54h-7.4V7.5z M193.7,0v7.5h-29.5V0H193.7z M164.2,61.3V54h29.5v7.4H164.2z M201.1,54h-7.4V39.2H179v-7.4
|
fill="none"
|
||||||
h22.1V54z M201.1,7.5v7.4h-7.4V7.5H201.1z"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
/>
|
className={className}
|
||||||
<path d="M283.5,61.3h-8.6L270.4,49h-30.8L235,61.3h-8.4L250.4,0h9.3L283.5,61.3z M254.8,7.4L242.2,42h25.6L255,7.4H254.8z" />
|
>
|
||||||
|
<path d="M8.05624 16.4619L13.3478 0.0158333H16.0045L9.45138 18.9881H6.58031L0 0.0158333H2.70328L8.05624 16.4619ZM27.1835 10.3067V16.8372H37.5787V18.9873H24.6884V0.0150417H37.3503V2.16442H27.1835V8.16288H36.2395V10.3067H27.1835ZM47.5793 2.31167H49.8165V16.716H47.5793V2.31167V2.31167ZM58.7676 0V2.31167H49.8157V0H58.7683H58.7676ZM49.8157 19V16.716H58.7683V19H49.8157ZM61.0055 16.716H58.7683V12.1481H54.2924V9.86021H61.0055V16.716ZM61.0055 2.31167V4.59483H58.7683V2.31167H61.0055V2.31167ZM86 18.9873H83.3946L82.0251 15.1668H72.6801L71.3106 18.9873H68.7635L75.9769 0.0150417H78.7936L86 18.9873V18.9873ZM77.3138 2.299L73.4694 13.0213H81.239L77.3674 2.29821H77.313L77.3138 2.299Z" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,18 @@
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { Vega } from '../icons/vega';
|
import { Vega } from '../icons/vega';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { AnchorButton } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
export const Navbar = () => {
|
export const Navbar = () => {
|
||||||
const navClasses = classNames(
|
|
||||||
'flex items-center',
|
|
||||||
'border-neutral-200 border-b'
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<nav className={navClasses}>
|
<nav className="flex items-center">
|
||||||
<Link href="/" passHref={true}>
|
<Link href="/" passHref={true}>
|
||||||
<a className="px-8">
|
<a className="px-[26px]">
|
||||||
<Vega />
|
<Vega className="fill-black dark:fill-white" />
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
{[
|
{[
|
||||||
|
{ name: 'Trading', path: '/', exact: true },
|
||||||
{ name: 'Portfolio', path: '/portfolio' },
|
{ name: 'Portfolio', path: '/portfolio' },
|
||||||
{ name: 'Markets', path: '/markets' },
|
{ name: 'Markets', path: '/markets' },
|
||||||
].map((route) => (
|
].map((route) => (
|
||||||
@ -28,18 +25,17 @@ export const Navbar = () => {
|
|||||||
interface NavLinkProps {
|
interface NavLinkProps {
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
|
exact?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NavLink = ({ name, path }: NavLinkProps) => {
|
const NavLink = ({ name, path, exact }: NavLinkProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const className = classNames('inline-block', 'p-8', {
|
const isActive =
|
||||||
// Handle direct math and child page matches
|
router.asPath === path || (!exact && router.asPath.startsWith(path));
|
||||||
'text-vega-pink': router.asPath === path || router.asPath.startsWith(path),
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<AnchorButton
|
||||||
className={className}
|
variant={isActive ? 'accent' : 'inline'}
|
||||||
|
className="px-16 h-[38px] text-h4 uppercase border-0"
|
||||||
href={path}
|
href={path}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -47,6 +43,6 @@ const NavLink = ({ name, path }: NavLinkProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
</a>
|
</AnchorButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,20 +1,45 @@
|
|||||||
import { ApolloProvider } from '@apollo/client';
|
import { ApolloProvider } from '@apollo/client';
|
||||||
import { AppProps } from 'next/app';
|
import { AppProps } from 'next/app';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import { useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { Navbar } from '../components/navbar';
|
import { Navbar } from '../components/navbar';
|
||||||
import { createClient } from '../lib/apollo-client';
|
import { createClient } from '../lib/apollo-client';
|
||||||
|
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
|
|
||||||
function VegaTradingApp({ Component, pageProps }: AppProps) {
|
function VegaTradingApp({ Component, pageProps }: AppProps) {
|
||||||
const client = useMemo(() => createClient(process.env['NX_VEGA_URL']), []);
|
const client = useMemo(() => createClient(process.env['NX_VEGA_URL']), []);
|
||||||
|
useCallback(() => {
|
||||||
|
if (
|
||||||
|
localStorage.theme === 'dark' ||
|
||||||
|
(!('theme' in localStorage) &&
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||||
|
) {
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove('dark');
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
const setTheme = () => {
|
||||||
|
localStorage.theme = document.documentElement.classList.toggle('dark')
|
||||||
|
? 'dark'
|
||||||
|
: undefined;
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Welcome to trading!</title>
|
<title>Welcome to trading!</title>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
href="https://vega.xyz/favicon-32x32.png"
|
||||||
|
type="image/png"
|
||||||
|
/>
|
||||||
</Head>
|
</Head>
|
||||||
<div className="h-full grid grid-rows-[min-content,_1fr]">
|
<div className="h-full dark:bg-black dark:text-white-60 bg-white text-black-60">
|
||||||
<Navbar />
|
<div className="flex items-center border-b-[7px] border-vega-yellow">
|
||||||
|
<Navbar />
|
||||||
|
<ThemeSwitcher onToggle={setTheme} className="ml-auto mr-8 -my-2" />
|
||||||
|
</div>
|
||||||
<main>
|
<main>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</main>
|
</main>
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import { EtherscanLink } from '@vegaprotocol/ui-toolkit';
|
import { Callout, Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Callout } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { ReactHelpers } from '@vegaprotocol/react-helpers';
|
|
||||||
|
|
||||||
export function Index() {
|
export function Index() {
|
||||||
/*
|
/*
|
||||||
@ -9,12 +7,20 @@ export function Index() {
|
|||||||
* Note: The corresponding styles are in the ./index.scss file.
|
* Note: The corresponding styles are in the ./index.scss file.
|
||||||
*/
|
*/
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="m-24 ">
|
||||||
<Callout title="Hello there" headingLevel={1}>
|
<Callout
|
||||||
Welcome trading 👋
|
intent="help"
|
||||||
|
title="This is what this thing does"
|
||||||
|
iconName="endorsed"
|
||||||
|
headingLevel={1}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div>With a longer explaination</div>
|
||||||
|
<Button className="block mt-8" variant="secondary">
|
||||||
|
Action
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</Callout>
|
</Callout>
|
||||||
<EtherscanLink chainId={null} address="address" />
|
|
||||||
<ReactHelpers />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -44,26 +44,26 @@ export const GridTabs = ({ children, group }: GridTabsProps) => {
|
|||||||
className="h-full grid grid-rows-[min-content_1fr]"
|
className="h-full grid grid-rows-[min-content_1fr]"
|
||||||
onValueChange={(value) => setActiveTab(value)}
|
onValueChange={(value) => setActiveTab(value)}
|
||||||
>
|
>
|
||||||
<Tabs.List className="flex gap-[2px] bg-neutral-200" role="tablist">
|
<Tabs.List
|
||||||
|
className="flex flex-nowrap gap-4 overflow-x-auto my-4"
|
||||||
|
role="tablist"
|
||||||
|
>
|
||||||
{Children.map(children, (child) => {
|
{Children.map(children, (child) => {
|
||||||
if (!isValidElement(child)) return null;
|
if (!isValidElement(child)) return null;
|
||||||
const isActive = child.props.name === activeTab;
|
const isActive = child.props.name === activeTab;
|
||||||
const triggerClass = classNames(
|
const triggerClass = classNames('py-4', 'px-12', 'capitalize', {
|
||||||
'py-4',
|
'text-black dark:text-vega-yellow': isActive,
|
||||||
'px-12',
|
'bg-white dark:bg-black': isActive,
|
||||||
'border-t border-neutral-200',
|
'text-black dark:text-white': !isActive,
|
||||||
'capitalize',
|
'bg-black-10 dark:bg-white-10': !isActive,
|
||||||
{
|
});
|
||||||
'text-vega-pink': isActive,
|
|
||||||
'bg-white': isActive,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<Tabs.Trigger value={child.props.name} className={triggerClass}>
|
<Tabs.Trigger value={child.props.name} className={triggerClass}>
|
||||||
{child.props.name}
|
{child.props.name}
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
<div className="bg-black-10 dark:bg-white-10 grow"></div>
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
<div className="h-full overflow-auto">
|
<div className="h-full overflow-auto">
|
||||||
{Children.map(children, (child) => {
|
{Children.map(children, (child) => {
|
||||||
|
@ -18,7 +18,7 @@ export const TradeGrid = ({ market }: TradeGridProps) => {
|
|||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div className={wrapperClasses}>
|
<div className={wrapperClasses}>
|
||||||
<header className="col-start-1 col-end-2 row-start-1 row-end-1 bg-white p-8">
|
<header className="col-start-1 col-end-2 row-start-1 row-end-1 p-8">
|
||||||
<h1>Market: {market.name}</h1>
|
<h1>Market: {market.name}</h1>
|
||||||
</header>
|
</header>
|
||||||
<TradeGridChild className="col-start-1 col-end-2">
|
<TradeGridChild className="col-start-1 col-end-2">
|
||||||
@ -93,7 +93,7 @@ export const TradePanels = ({ market }: TradePanelsProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full grid grid-rows-[min-content_1fr_min-content]">
|
<div className="h-full grid grid-rows-[min-content_1fr_min-content]">
|
||||||
<header className="bg-white p-8">
|
<header className="p-8">
|
||||||
<h1>Market: {market.name}</h1>
|
<h1>Market: {market.name}</h1>
|
||||||
</header>
|
</header>
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
@ -103,18 +103,15 @@ export const TradePanels = ({ market }: TradePanelsProps) => {
|
|||||||
)}
|
)}
|
||||||
</AutoSizer>
|
</AutoSizer>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-nowrap gap-2 bg-neutral-200 border-neutral-200 border-t overflow-x-auto">
|
<div className="flex flex-nowrap gap-4 overflow-x-auto my-4">
|
||||||
{Object.keys(TradingViews).map((key: TradingView) => {
|
{Object.keys(TradingViews).map((key: TradingView) => {
|
||||||
const className = classNames(
|
const isActive = view === key;
|
||||||
'p-8',
|
const className = classNames('py-4', 'px-12', 'capitalize', {
|
||||||
'border-t',
|
'text-black dark:text-vega-yellow': isActive,
|
||||||
'border-neutral-200',
|
'bg-white dark:bg-black': isActive,
|
||||||
'capitalize',
|
'text-black dark:text-white': !isActive,
|
||||||
{
|
'bg-black-10 dark:bg-white-10': !isActive,
|
||||||
'text-vega-pink': view === key,
|
});
|
||||||
'bg-white': view === key,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={() => setView(key)}
|
onClick={() => setView(key)}
|
||||||
@ -125,6 +122,7 @@ export const TradePanels = ({ market }: TradePanelsProps) => {
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
<div className="bg-black-10 dark:bg-white-10 grow"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -8,72 +8,48 @@ module.exports = {
|
|||||||
colors: {
|
colors: {
|
||||||
transparent: 'transparent',
|
transparent: 'transparent',
|
||||||
current: 'currentColor',
|
current: 'currentColor',
|
||||||
black: '#000',
|
white: {
|
||||||
white: '#FFF',
|
DEFAULT: '#FFF',
|
||||||
|
'02': 'rgba(255, 255, 255, 0.02)',
|
||||||
neutral: {
|
'05': 'rgba(255, 255, 255, 0.05)',
|
||||||
// 250 - 23 = 227; (900-50) / 227 = 850 / 227 = 3.74449339207
|
10: 'rgba(255, 255, 255, 0.10)',
|
||||||
50: '#fafafa', // FA = 250
|
25: 'rgba(255, 255, 255, 0.25)',
|
||||||
100: '#ebebeb',
|
40: 'rgba(255, 255, 255, 0.40)',
|
||||||
150: '#dcdcdc',
|
60: 'rgba(255, 255, 255, 0.60)',
|
||||||
200: '#cdcdcd',
|
80: 'rgba(255, 255, 255, 0.80)',
|
||||||
250: '#bebebe',
|
95: 'rgba(255, 255, 255, 0.95)',
|
||||||
300: '#afafaf',
|
100: 'rgba(255, 255, 255, 1.00)',
|
||||||
350: '#a1a1a1',
|
|
||||||
400: '#939393',
|
|
||||||
450: '#858585',
|
|
||||||
500: '#787878',
|
|
||||||
550: '#6a6a6a',
|
|
||||||
593: '#696969', // dark muted
|
|
||||||
600: '#5d5d5d',
|
|
||||||
650: '#515151',
|
|
||||||
700: '#444444',
|
|
||||||
753: '#3E3E3E', // dark -> 3F is muted
|
|
||||||
750: '#383838',
|
|
||||||
800: '#2d2d2d', // breakdown-background was 2C
|
|
||||||
850: '#222222',
|
|
||||||
900: '#171717', // 17 = 23
|
|
||||||
},
|
},
|
||||||
|
black: {
|
||||||
'light-gray-50': '#F5F8FA', //off-white - https://blueprintjs.com/docs/#core/colors
|
DEFAULT: '#000',
|
||||||
'gray-50': '#BFCCD6', // muted - https://blueprintjs.com/docs/#core/colors
|
'02': 'rgba(0, 0, 0, 0.02)',
|
||||||
|
'05': 'rgba(0, 0, 0, 0.05)',
|
||||||
|
10: 'rgba(0, 0, 0, 0.10)',
|
||||||
|
25: 'rgba(0, 0, 0, 0.25)',
|
||||||
|
40: 'rgba(0, 0, 0, 0.40)',
|
||||||
|
60: 'rgba(0, 0, 0, 0.60)',
|
||||||
|
80: 'rgba(0, 0, 0, 0.80)',
|
||||||
|
95: 'rgba(0, 0, 0, 0.95)',
|
||||||
|
100: 'rgba(0, 0, 0, 1)',
|
||||||
|
},
|
||||||
|
blue: '#1DA2FB',
|
||||||
coral: '#FF6057',
|
coral: '#FF6057',
|
||||||
// below colors are not defined as atoms
|
|
||||||
vega: {
|
vega: {
|
||||||
yellow: '#EDFF22',
|
yellow: '#EDFF22',
|
||||||
pink: '#FF2D5E',
|
pink: '#FF2D5E',
|
||||||
green: '#00F780',
|
green: '#00F780',
|
||||||
},
|
},
|
||||||
|
'vega-yellow-dark': '#474B0A', // yellow 0.3 opacity on black
|
||||||
intent: {
|
intent: {
|
||||||
danger: '#FF261A',
|
danger: '#FF261A',
|
||||||
warning: '#FF7A1A',
|
warning: '#FF7A1A',
|
||||||
prompt: '#EDFF22',
|
prompt: '#EDFF22',
|
||||||
progress: '#FFF',
|
|
||||||
success: '#26FF8A',
|
success: '#26FF8A',
|
||||||
help: '#494949',
|
help: '#494949',
|
||||||
background: {
|
},
|
||||||
danger: '#9E0025', // for white text
|
'intent-background': {
|
||||||
},
|
danger: '#9E0025', // for white text
|
||||||
} /*,
|
},
|
||||||
data: {
|
|
||||||
red: {
|
|
||||||
white: {
|
|
||||||
50: '#FFFFFF',
|
|
||||||
220: '#FF6057', // overlay FFF 80%
|
|
||||||
390: '#FF6057', // overlay FFF 60%
|
|
||||||
560: '#FF6057', // overlay FFF 40%
|
|
||||||
730: '#FF6057', // overlay FFF 20%
|
|
||||||
900: '#FF6057',
|
|
||||||
},
|
|
||||||
green: {
|
|
||||||
50: '#30F68B',
|
|
||||||
220: '#89DC50',
|
|
||||||
475: '#F2BD09',
|
|
||||||
730: '#FF8501',
|
|
||||||
900: '#FF6057',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},*/,
|
|
||||||
},
|
},
|
||||||
spacing: {
|
spacing: {
|
||||||
0: '0px',
|
0: '0px',
|
||||||
@ -81,18 +57,37 @@ module.exports = {
|
|||||||
4: '0.25rem',
|
4: '0.25rem',
|
||||||
8: '0.5rem',
|
8: '0.5rem',
|
||||||
12: '0.75rem',
|
12: '0.75rem',
|
||||||
|
16: '1rem',
|
||||||
|
20: '1.25rem',
|
||||||
|
24: '1.5rem',
|
||||||
28: '1.75rem',
|
28: '1.75rem',
|
||||||
|
32: '2rem',
|
||||||
44: '2.75rem',
|
44: '2.75rem',
|
||||||
},
|
},
|
||||||
backgroundColor: ({ theme }) => ({
|
opacity: {
|
||||||
transparent: 'transparent',
|
0: '0',
|
||||||
neutral: theme('colors.neutral'),
|
2: '0.02',
|
||||||
dark: theme('colors.dark'),
|
5: '0.05',
|
||||||
black: '#000',
|
10: '0.1',
|
||||||
white: theme('colors.white'),
|
15: '0.15',
|
||||||
danger: theme('colors.intent.background.danger'),
|
20: '0.2',
|
||||||
'neutral-200': theme('colors.neutral.200'),
|
25: '0.25',
|
||||||
}),
|
30: '0.3',
|
||||||
|
35: '0.35',
|
||||||
|
40: '0.4',
|
||||||
|
45: '0.45',
|
||||||
|
50: '0.5',
|
||||||
|
55: '0.55',
|
||||||
|
60: '0.6',
|
||||||
|
65: '0.65',
|
||||||
|
70: '0.7',
|
||||||
|
75: '0.75',
|
||||||
|
80: '0.8',
|
||||||
|
85: '0.85',
|
||||||
|
90: '0.9',
|
||||||
|
95: '0.95',
|
||||||
|
100: '1',
|
||||||
|
},
|
||||||
borderWidth: {
|
borderWidth: {
|
||||||
DEFAULT: '1px',
|
DEFAULT: '1px',
|
||||||
1: '1px',
|
1: '1px',
|
||||||
@ -132,12 +127,12 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
fontSize: {
|
fontSize: {
|
||||||
h1: ['72px', { lineHeight: '92px', letterSpacing: '-1%' }],
|
h1: ['72px', { lineHeight: '92px', letterSpacing: '-0.01em' }],
|
||||||
h2: ['48px', { lineHeight: '64px', letterSpacing: '-1%' }],
|
h2: ['48px', { lineHeight: '64px', letterSpacing: '-0.01em' }],
|
||||||
h3: ['32px', { lineHeight: '40px', letterSpacing: '-1%' }],
|
h3: ['32px', { lineHeight: '40px', letterSpacing: '-0.01em' }],
|
||||||
|
|
||||||
h4: ['24px', { lineHeight: '36px', letterSpacing: '-1%' }],
|
h4: ['24px', { lineHeight: '36px', letterSpacing: '-0.01em' }],
|
||||||
h5: ['18px', { lineHeight: '28px', letterSpacing: '-1%' }],
|
h5: ['18px', { lineHeight: '28px', letterSpacing: '-0.01em' }],
|
||||||
|
|
||||||
'body-large': ['16px', '24px'],
|
'body-large': ['16px', '24px'],
|
||||||
body: ['14px', '20px'],
|
body: ['14px', '20px'],
|
||||||
@ -148,7 +143,9 @@ module.exports = {
|
|||||||
|
|
||||||
extend: {
|
extend: {
|
||||||
boxShadow: {
|
boxShadow: {
|
||||||
callout: '5px 5px 0 1px rgba(0, 0, 0, 0.05)',
|
callout: '5px 5px 0 1px rgba(255, 255, 255, 0.05)',
|
||||||
|
focus: '0px 0px 0px 1px #FFFFFF, 0px 0px 3px 2px #FFE600',
|
||||||
|
'focus-dark': '0px 0px 0px 1px #000000, 0px 0px 3px 2px #FFE600',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,19 +1,29 @@
|
|||||||
import '../src/styles.scss';
|
import '../src/styles.scss';
|
||||||
export const parameters = {
|
export const parameters = {
|
||||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||||
themes: {
|
/*themes: {
|
||||||
default: 'dark',
|
default: 'dark',
|
||||||
list: [
|
list: [
|
||||||
{ name: 'dark', class: ['dark', 'bg-black'], color: '#000' },
|
{ name: 'dark', class: ['dark', 'bg-black'], color: '#000' },
|
||||||
{ name: 'light', class: '', color: '#FFF' },
|
{ name: 'light', class: '', color: '#FFF' },
|
||||||
],
|
],
|
||||||
},
|
},*/
|
||||||
};
|
};
|
||||||
|
|
||||||
export const decorators = [
|
export const decorators = [
|
||||||
(Story) => (
|
(Story, context) =>
|
||||||
<div className="dark bg-black">
|
context.parameters.themes === false ? (
|
||||||
<Story />
|
<div className="text-body">
|
||||||
</div>
|
<Story />
|
||||||
),
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-body">
|
||||||
|
<div className="dark bg-black p-16">
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
<div className="p-16">
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
10
libs/ui-toolkit/src/components/button/button.spec.tsx
Normal file
10
libs/ui-toolkit/src/components/button/button.spec.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { Button } from './button';
|
||||||
|
|
||||||
|
describe('Button', () => {
|
||||||
|
it('should render successfully', () => {
|
||||||
|
const { baseElement } = render(<Button>Label</Button>);
|
||||||
|
expect(baseElement).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
101
libs/ui-toolkit/src/components/button/button.stories.tsx
Normal file
101
libs/ui-toolkit/src/components/button/button.stories.tsx
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import { Story, Meta } from '@storybook/react';
|
||||||
|
import { Button } from './button';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
component: Button,
|
||||||
|
title: 'Button',
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
const Template: Story = (args) => (
|
||||||
|
<>
|
||||||
|
<div className="mb-8">
|
||||||
|
<Button {...args} />
|
||||||
|
</div>
|
||||||
|
{args['variant'] !== 'inline' && <Button {...args} disabled />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Primary = Template.bind({});
|
||||||
|
Primary.args = {
|
||||||
|
children: 'Primary',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Secondary = Template.bind({});
|
||||||
|
Secondary.args = {
|
||||||
|
children: 'Secondary',
|
||||||
|
variant: 'secondary',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Accent = Template.bind({});
|
||||||
|
Accent.args = {
|
||||||
|
children: 'Accent',
|
||||||
|
variant: 'accent',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Inline = Template.bind({});
|
||||||
|
Inline.args = {
|
||||||
|
children: 'Inline',
|
||||||
|
variant: 'inline',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NavAccent: Story = (args) => (
|
||||||
|
<>
|
||||||
|
<div className="mb-8">
|
||||||
|
<Button variant="accent" className="px-4">
|
||||||
|
Background
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="mb-8">
|
||||||
|
<Button variant="accent" className="px-4" prependIconName="menu-open">
|
||||||
|
Background
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="mb-8">
|
||||||
|
<Button variant="accent" className="px-4" appendIconName="menu-closed">
|
||||||
|
Background
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const NavInline: Story = (args) => (
|
||||||
|
<>
|
||||||
|
<div className="mb-8">
|
||||||
|
<Button variant="inline" className="uppercase">
|
||||||
|
Background
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="mb-8">
|
||||||
|
<Button
|
||||||
|
variant="inline"
|
||||||
|
className="uppercase"
|
||||||
|
prependIconName="menu-open"
|
||||||
|
>
|
||||||
|
Background
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="mb-8">
|
||||||
|
<Button
|
||||||
|
variant="inline"
|
||||||
|
className="uppercase"
|
||||||
|
appendIconName="menu-closed"
|
||||||
|
>
|
||||||
|
Background
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const IconPrepend = Template.bind({});
|
||||||
|
IconPrepend.args = {
|
||||||
|
children: 'Icon prepend',
|
||||||
|
prependIconName: 'search',
|
||||||
|
variant: 'accent',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IconAppend = Template.bind({});
|
||||||
|
IconAppend.args = {
|
||||||
|
children: 'Icon append',
|
||||||
|
appendIconName: 'search',
|
||||||
|
variant: 'accent',
|
||||||
|
};
|
165
libs/ui-toolkit/src/components/button/button.tsx
Normal file
165
libs/ui-toolkit/src/components/button/button.tsx
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import { AnchorHTMLAttributes, ButtonHTMLAttributes, forwardRef } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { Icon, IconName } from '../icon';
|
||||||
|
import {
|
||||||
|
includesLeftPadding,
|
||||||
|
includesRightPadding,
|
||||||
|
includesBorderWidth,
|
||||||
|
} from '../../utils/class-names';
|
||||||
|
|
||||||
|
interface CommonProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
variant?: 'primary' | 'secondary' | 'accent' | 'inline';
|
||||||
|
className?: string;
|
||||||
|
prependIconName?: IconName;
|
||||||
|
appendIconName?: IconName;
|
||||||
|
}
|
||||||
|
export interface ButtonProps
|
||||||
|
extends ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
|
CommonProps {}
|
||||||
|
|
||||||
|
export interface AnchorButtonProps
|
||||||
|
extends AnchorHTMLAttributes<HTMLAnchorElement>,
|
||||||
|
CommonProps {}
|
||||||
|
|
||||||
|
const getClassName = (
|
||||||
|
className: CommonProps['className'],
|
||||||
|
variant: CommonProps['variant']
|
||||||
|
) => {
|
||||||
|
const paddingLeftProvided = includesLeftPadding(className);
|
||||||
|
const paddingRightProvided = includesRightPadding(className);
|
||||||
|
const borderWidthProvided = includesBorderWidth(className);
|
||||||
|
return classNames(
|
||||||
|
[
|
||||||
|
'inline-flex',
|
||||||
|
'items-center',
|
||||||
|
'justify-center',
|
||||||
|
'box-border',
|
||||||
|
'h-28',
|
||||||
|
'text-ui',
|
||||||
|
'no-underline',
|
||||||
|
'hover:underline',
|
||||||
|
'disabled:no-underline',
|
||||||
|
'transition-all',
|
||||||
|
],
|
||||||
|
{
|
||||||
|
'pl-28': !(paddingLeftProvided || variant === 'inline'),
|
||||||
|
'pr-28': !(paddingRightProvided || variant === 'inline'),
|
||||||
|
|
||||||
|
border: !borderWidthProvided,
|
||||||
|
|
||||||
|
'hover:border-black dark:hover:border-white': variant !== 'inline',
|
||||||
|
'active:border-black dark:active:border-white': true,
|
||||||
|
|
||||||
|
'bg-black dark:bg-white': variant === 'primary',
|
||||||
|
'border-black-60 dark:border-white-60':
|
||||||
|
variant === 'primary' || variant === 'secondary',
|
||||||
|
'text-white dark:text-black': variant === 'primary',
|
||||||
|
'hover:bg-black-80 dark:hover:bg-white-80': variant === 'primary',
|
||||||
|
'active:bg-white dark:active:bg-black':
|
||||||
|
variant === 'primary' || variant === 'accent',
|
||||||
|
'active:text-black dark:active:text-white':
|
||||||
|
variant === 'primary' || variant === 'accent',
|
||||||
|
|
||||||
|
'bg-white dark:bg-black': variant === 'secondary',
|
||||||
|
'text-black dark:text-white': variant === 'secondary',
|
||||||
|
'hover:bg-black-25 dark:hover:bg-white-25': variant === 'secondary',
|
||||||
|
'hover:text-black dark:hover:text-white':
|
||||||
|
variant === 'secondary' || variant === 'accent',
|
||||||
|
'active:bg-black dark:active:bg-white': variant === 'secondary',
|
||||||
|
'active:text-white dark:active:text-black': variant === 'secondary',
|
||||||
|
|
||||||
|
uppercase: variant === 'accent',
|
||||||
|
'bg-vega-yellow dark:bg-vega-yellow': variant === 'accent',
|
||||||
|
'border-transparent dark:border-transparent':
|
||||||
|
variant === 'accent' || variant === 'inline',
|
||||||
|
'hover:bg-vega-yellow-dark dark:hover:bg-vega-yellow/30':
|
||||||
|
variant === 'accent',
|
||||||
|
'text-black dark:text-black': variant === 'accent',
|
||||||
|
'hover:text-white dark:hover:text-white': variant === 'accent',
|
||||||
|
|
||||||
|
'pl-4': variant === 'inline' && !paddingLeftProvided,
|
||||||
|
'pr-4': variant === 'inline' && !paddingRightProvided,
|
||||||
|
'border-0': variant === 'inline' && !borderWidthProvided,
|
||||||
|
underline: variant === 'inline',
|
||||||
|
'hover:no-underline': variant === 'inline',
|
||||||
|
'hover:border-transparent dark:hover:border-transparent':
|
||||||
|
variant === 'inline',
|
||||||
|
'active:border-transparent dark:active:border-transparent':
|
||||||
|
variant === 'inline',
|
||||||
|
'active:text-black dark:active:text-vega-yellow': variant === 'inline',
|
||||||
|
'text-black-95 dark:text-white-95': variant === 'inline',
|
||||||
|
'hover:text-black hover:dark:text-white': variant === 'inline',
|
||||||
|
|
||||||
|
'disabled:bg-black-10 dark:disabled:bg-white-10': variant !== 'inline',
|
||||||
|
'disabled:text-black-60 dark:disabled:text-white-60':
|
||||||
|
variant !== 'inline',
|
||||||
|
'disabled:border-black-25 dark:disabled:border-white-25':
|
||||||
|
variant !== 'inline',
|
||||||
|
},
|
||||||
|
className
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getContent = (
|
||||||
|
children: React.ReactNode,
|
||||||
|
prependIconName?: IconName,
|
||||||
|
appendIconName?: IconName
|
||||||
|
) => {
|
||||||
|
const iconName = prependIconName || appendIconName;
|
||||||
|
if (iconName === undefined) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
const iconClassName = classNames(['fill-current'], {
|
||||||
|
'mr-8': prependIconName,
|
||||||
|
'ml-8': appendIconName,
|
||||||
|
});
|
||||||
|
const icon = <Icon name={iconName} className={iconClassName} size={16} />;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{prependIconName && icon}
|
||||||
|
{children}
|
||||||
|
{appendIconName && icon}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
variant = 'primary',
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
prependIconName,
|
||||||
|
appendIconName,
|
||||||
|
...props
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<button ref={ref} className={getClassName(className, variant)} {...props}>
|
||||||
|
{getContent(children, prependIconName, appendIconName)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const AnchorButton = forwardRef<HTMLAnchorElement, AnchorButtonProps>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
variant = 'primary',
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
prependIconName,
|
||||||
|
appendIconName,
|
||||||
|
...prosp
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<a ref={ref} className={getClassName(className, variant)} {...prosp}>
|
||||||
|
{getContent(children, prependIconName, appendIconName)}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
1
libs/ui-toolkit/src/components/button/index.ts
Normal file
1
libs/ui-toolkit/src/components/button/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './button';
|
@ -1,58 +0,0 @@
|
|||||||
@import '../../styles/colors';
|
|
||||||
|
|
||||||
.callout {
|
|
||||||
display: flex;
|
|
||||||
padding: 14px;
|
|
||||||
border: 1px solid $white;
|
|
||||||
box-shadow: 3px 3px 0px $white;
|
|
||||||
margin: 12px 0;
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0 0 10px 0;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VARIATIONS
|
|
||||||
&--error {
|
|
||||||
box-shadow: 5px 5px 0px $vega-red3;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--success {
|
|
||||||
box-shadow: 5px 5px 0px $vega-green3;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--warn {
|
|
||||||
box-shadow: 5px 5px 0px $vega-orange3;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--action {
|
|
||||||
box-shadow: 5px 5px 0px $vega-yellow3;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
> :last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__title,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__icon {
|
|
||||||
margin-right: 10px;
|
|
||||||
color: $white;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||||
|
|
||||||
import { Callout } from '.';
|
import { Callout } from './callout';
|
||||||
|
import { Button } from '../button';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Callout',
|
title: 'Callout',
|
||||||
@ -9,37 +10,61 @@ export default {
|
|||||||
} as ComponentMeta<typeof Callout>;
|
} as ComponentMeta<typeof Callout>;
|
||||||
|
|
||||||
const Template: ComponentStory<typeof Callout> = (args) => (
|
const Template: ComponentStory<typeof Callout> = (args) => (
|
||||||
<Callout {...args}>Content</Callout>
|
<Callout {...args} />
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Default = Template.bind({});
|
export const Default = Template.bind({});
|
||||||
|
Default.args = {
|
||||||
|
children: 'Content',
|
||||||
|
};
|
||||||
|
|
||||||
export const Danger = Template.bind({});
|
export const Danger = Template.bind({});
|
||||||
Danger.args = {
|
Danger.args = {
|
||||||
intent: 'danger',
|
intent: 'danger',
|
||||||
|
children: 'Content',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Warning = Template.bind({});
|
export const Warning = Template.bind({});
|
||||||
Warning.args = {
|
Warning.args = {
|
||||||
intent: 'warning',
|
intent: 'warning',
|
||||||
|
children: 'Content',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Prompt = Template.bind({});
|
export const Prompt = Template.bind({});
|
||||||
Prompt.args = {
|
Prompt.args = {
|
||||||
intent: 'prompt',
|
intent: 'prompt',
|
||||||
|
children: 'Content',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Progress = Template.bind({});
|
export const Progress = Template.bind({});
|
||||||
Progress.args = {
|
Progress.args = {
|
||||||
intent: 'progress',
|
intent: 'progress',
|
||||||
|
children: 'Content',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Success = Template.bind({});
|
export const Success = Template.bind({});
|
||||||
Success.args = {
|
Success.args = {
|
||||||
intent: 'success',
|
intent: 'success',
|
||||||
|
children: 'Content',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Help = Template.bind({});
|
export const Help = Template.bind({});
|
||||||
Help.args = {
|
Help.args = {
|
||||||
intent: 'help',
|
intent: 'help',
|
||||||
|
children: 'Content',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IconAndContent = Template.bind({});
|
||||||
|
IconAndContent.args = {
|
||||||
|
intent: 'help',
|
||||||
|
title: 'This is what this thing does',
|
||||||
|
iconName: 'endorsed',
|
||||||
|
children: (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div>With a longer explaination</div>
|
||||||
|
<Button className="block mt-8" variant="secondary">
|
||||||
|
Action
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
@ -8,8 +8,10 @@ test('It renders content within callout', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('It renders title and icon', () => {
|
test('It renders title and icon', () => {
|
||||||
render(<Callout icon={<div data-testid="icon" />} title="title" />);
|
render(<Callout iconName="endorsed" title="title" />);
|
||||||
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
expect(
|
||||||
|
screen.getByTestId('callout').querySelector('svg')
|
||||||
|
).toBeInTheDocument();
|
||||||
expect(screen.getByText('title')).toBeInTheDocument();
|
expect(screen.getByText('title')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ intents.map((intent) =>
|
|||||||
test(`Applies class for progress`, () => {
|
test(`Applies class for progress`, () => {
|
||||||
render(<Callout intent="progress" />);
|
render(<Callout intent="progress" />);
|
||||||
expect(screen.getByTestId('callout')).toHaveClass(
|
expect(screen.getByTestId('callout')).toHaveClass(
|
||||||
'shadow-intent-black',
|
'shadow-black',
|
||||||
'dark:shadow-intent-progress'
|
'dark:shadow-white'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,48 +1,55 @@
|
|||||||
import React from 'react';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { Icon, IconName } from '../icon';
|
||||||
|
|
||||||
export const Callout = ({
|
export interface CalloutProps {
|
||||||
children,
|
|
||||||
title,
|
|
||||||
intent = 'help',
|
|
||||||
icon,
|
|
||||||
headingLevel,
|
|
||||||
}: {
|
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
title?: React.ReactElement | string;
|
title?: React.ReactElement | string;
|
||||||
intent?: 'danger' | 'warning' | 'prompt' | 'progress' | 'success' | 'help';
|
intent?: 'danger' | 'warning' | 'prompt' | 'progress' | 'success' | 'help';
|
||||||
icon?: React.ReactNode;
|
iconName?: IconName;
|
||||||
headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
|
headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
|
||||||
}) => {
|
}
|
||||||
|
|
||||||
|
export function Callout({
|
||||||
|
children,
|
||||||
|
title,
|
||||||
|
intent = 'help',
|
||||||
|
iconName,
|
||||||
|
headingLevel,
|
||||||
|
}: CalloutProps) {
|
||||||
const className = classNames(
|
const className = classNames(
|
||||||
'shadow-callout',
|
'shadow-callout',
|
||||||
'border',
|
'border',
|
||||||
'border-black',
|
'border-black',
|
||||||
'dark:border-white',
|
'dark:border-white',
|
||||||
|
'text-body-large',
|
||||||
|
'dark:text-white',
|
||||||
'p-8',
|
'p-8',
|
||||||
{
|
{
|
||||||
'shadow-intent-danger': intent === 'danger',
|
'shadow-intent-danger': intent === 'danger',
|
||||||
'shadow-intent-warning': intent === 'warning',
|
'shadow-intent-warning': intent === 'warning',
|
||||||
'shadow-intent-prompt': intent === 'prompt',
|
'shadow-intent-prompt': intent === 'prompt',
|
||||||
'shadow-intent-black dark:shadow-intent-progress': intent === 'progress',
|
'shadow-black dark:shadow-white': intent === 'progress',
|
||||||
'shadow-intent-success': intent === 'success',
|
'shadow-intent-success': intent === 'success',
|
||||||
'shadow-intent-help': intent === 'help',
|
'shadow-intent-help': intent === 'help',
|
||||||
flex: icon,
|
flex: !!iconName,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const TitleTag: keyof JSX.IntrinsicElements = headingLevel
|
const TitleTag: keyof JSX.IntrinsicElements = headingLevel
|
||||||
? `h${headingLevel}`
|
? `h${headingLevel}`
|
||||||
: 'div';
|
: 'div';
|
||||||
|
const icon = iconName && (
|
||||||
|
<Icon name={iconName} className="fill-current ml-8 mr-16 mt-8" size={20} />
|
||||||
|
);
|
||||||
const body = (
|
const body = (
|
||||||
<div className="body-large dark:text-white">
|
<>
|
||||||
{title && <TitleTag className="text-h5">{title}</TitleTag>}
|
{title && <TitleTag className="text-h5">{title}</TitleTag>}
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div data-testid="callout" className={className}>
|
<div data-testid="callout" className={className}>
|
||||||
{icon && <div className="">{icon}</div>}
|
{icon}
|
||||||
{icon ? <div className="grow">{body}</div> : body}
|
{icon ? <div className="grow">{body}</div> : body}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import * as React from 'react';
|
|
||||||
import { EtherscanLink } from '.';
|
import { EtherscanLink } from '.';
|
||||||
import { EthereumChainIds } from '../../utils/web3';
|
import { EthereumChainIds } from '../../utils/web3';
|
||||||
|
|
||||||
|
10
libs/ui-toolkit/src/components/icon/icon.spec.tsx
Normal file
10
libs/ui-toolkit/src/components/icon/icon.spec.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { Icon } from './icon';
|
||||||
|
|
||||||
|
describe('Icon', () => {
|
||||||
|
it('should render successfully', () => {
|
||||||
|
const { baseElement } = render(<Icon name="add" />);
|
||||||
|
expect(baseElement).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
12
libs/ui-toolkit/src/components/icon/icon.stories.tsx
Normal file
12
libs/ui-toolkit/src/components/icon/icon.stories.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Story, Meta } from '@storybook/react';
|
||||||
|
import { Icon } from './icon';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
component: Icon,
|
||||||
|
title: 'Icon',
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
const Template: Story = (args) => <Icon {...args} name="warning-sign" />;
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
Default.args = {};
|
30
libs/ui-toolkit/src/components/icon/icon.tsx
Normal file
30
libs/ui-toolkit/src/components/icon/icon.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { IconSvgPaths20, IconSvgPaths16, IconName } from '@blueprintjs/icons';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
export type { IconName } from '@blueprintjs/icons';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
name: IconName;
|
||||||
|
className?: string;
|
||||||
|
size?: 16 | 20 | 24 | 32 | 48 | 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Icon = ({ size = 16, name, className }: IconProps) => {
|
||||||
|
const effectiveClassName = classNames(
|
||||||
|
{
|
||||||
|
'w-20': size === 20,
|
||||||
|
'h-20': size === 20,
|
||||||
|
'w-16': size === 16,
|
||||||
|
'h-16': size === 16,
|
||||||
|
},
|
||||||
|
className
|
||||||
|
);
|
||||||
|
const viewbox = size <= 16 ? '0 0 16 16' : '0 0 20 20';
|
||||||
|
return (
|
||||||
|
<svg className={effectiveClassName} viewBox={viewbox}>
|
||||||
|
{(size <= 16 ? IconSvgPaths16 : IconSvgPaths20)[name].map((d, key) => (
|
||||||
|
<path fillRule="evenodd" clipRule="evenodd" d={d} key={key} />
|
||||||
|
))}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
1
libs/ui-toolkit/src/components/icon/index.ts
Normal file
1
libs/ui-toolkit/src/components/icon/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './icon';
|
1
libs/ui-toolkit/src/components/input-error/index.ts
Normal file
1
libs/ui-toolkit/src/components/input-error/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './input-error';
|
@ -0,0 +1,10 @@
|
|||||||
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { InputError } from './input-error';
|
||||||
|
|
||||||
|
describe('InputError', () => {
|
||||||
|
it('should render successfully', () => {
|
||||||
|
const { baseElement } = render(<InputError />);
|
||||||
|
expect(baseElement).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,20 @@
|
|||||||
|
import { Story, Meta } from '@storybook/react';
|
||||||
|
import { InputError } from './input-error';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
component: InputError,
|
||||||
|
title: 'InputError',
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
const Template: Story = (args) => <InputError {...args} />;
|
||||||
|
|
||||||
|
export const Danger = Template.bind({});
|
||||||
|
Danger.args = {
|
||||||
|
children: 'An error that might have happened',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Warning = Template.bind({});
|
||||||
|
Warning.args = {
|
||||||
|
intent: 'warning',
|
||||||
|
children: 'Something that might be an issue',
|
||||||
|
};
|
41
libs/ui-toolkit/src/components/input-error/input-error.tsx
Normal file
41
libs/ui-toolkit/src/components/input-error/input-error.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import { Icon } from '../icon';
|
||||||
|
|
||||||
|
interface InputErrorProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
intent?: 'danger' | 'warning';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InputError = ({
|
||||||
|
intent = 'danger',
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
}: InputErrorProps) => {
|
||||||
|
const effectiveClassName = classNames(
|
||||||
|
[
|
||||||
|
'inline-flex',
|
||||||
|
'items-center',
|
||||||
|
'box-border',
|
||||||
|
'h-28',
|
||||||
|
'border-l-4',
|
||||||
|
'text-black-95 dark:text-white-95',
|
||||||
|
'text-ui',
|
||||||
|
],
|
||||||
|
{
|
||||||
|
'border-intent-danger': intent === 'danger',
|
||||||
|
'border-intent-warning': intent === 'warning',
|
||||||
|
},
|
||||||
|
className
|
||||||
|
);
|
||||||
|
const iconClassName = classNames(['mx-8'], {
|
||||||
|
'fill-intent-danger': intent === 'danger',
|
||||||
|
'fill-intent-warning': intent === 'warning',
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div className={effectiveClassName}>
|
||||||
|
<Icon name="warning-sign" className={iconClassName} />
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
1
libs/ui-toolkit/src/components/input/index.ts
Normal file
1
libs/ui-toolkit/src/components/input/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './input';
|
10
libs/ui-toolkit/src/components/input/input.spec.tsx
Normal file
10
libs/ui-toolkit/src/components/input/input.spec.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { Input } from './input';
|
||||||
|
|
||||||
|
describe('Input', () => {
|
||||||
|
it('should render successfully', () => {
|
||||||
|
const { baseElement } = render(<Input />);
|
||||||
|
expect(baseElement).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
29
libs/ui-toolkit/src/components/input/input.stories.tsx
Normal file
29
libs/ui-toolkit/src/components/input/input.stories.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { Story, Meta } from '@storybook/react';
|
||||||
|
import { Input } from './input';
|
||||||
|
export default {
|
||||||
|
component: Input,
|
||||||
|
title: 'Input',
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
const Template: Story = (args) => <Input {...args} value="I type words" />;
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
Default.args = {};
|
||||||
|
|
||||||
|
export const WithError = Template.bind({});
|
||||||
|
WithError.args = {
|
||||||
|
hasError: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Disabled = Template.bind({});
|
||||||
|
Disabled.args = {
|
||||||
|
disabled: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IconPrepend: Story = () => (
|
||||||
|
<Input value="I type words" prependIconName="search" />
|
||||||
|
);
|
||||||
|
|
||||||
|
export const IconAppend: Story = () => (
|
||||||
|
<Input value="I type words and even more words" appendIconName="search" />
|
||||||
|
);
|
98
libs/ui-toolkit/src/components/input/input.tsx
Normal file
98
libs/ui-toolkit/src/components/input/input.tsx
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { InputHTMLAttributes, forwardRef } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { Icon, IconName } from '../icon';
|
||||||
|
import {
|
||||||
|
includesLeftPadding,
|
||||||
|
includesRightPadding,
|
||||||
|
} from '../../utils/class-names';
|
||||||
|
|
||||||
|
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||||
|
hasError?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
className?: string;
|
||||||
|
prependIconName?: IconName;
|
||||||
|
appendIconName?: IconName;
|
||||||
|
}
|
||||||
|
export const inputClassNames = ({
|
||||||
|
hasError,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
hasError?: boolean;
|
||||||
|
className?: string;
|
||||||
|
}) => {
|
||||||
|
return classNames(
|
||||||
|
[
|
||||||
|
'inline-flex',
|
||||||
|
'items-center',
|
||||||
|
'box-border',
|
||||||
|
'border',
|
||||||
|
'bg-clip-padding',
|
||||||
|
'border-black-60 dark:border-white-60',
|
||||||
|
'bg-black-25 dark:bg-white-25',
|
||||||
|
'text-black-60 dark:text-white-60',
|
||||||
|
'text-ui',
|
||||||
|
'focus-visible:shadow-focus dark:focus-visible:shadow-focus-dark',
|
||||||
|
'focus-visible:outline-0',
|
||||||
|
'disabled:bg-black-10 disabled:dark:bg-white-10',
|
||||||
|
],
|
||||||
|
{
|
||||||
|
'pl-8': !includesLeftPadding(className),
|
||||||
|
'pr-8': !includesRightPadding(className),
|
||||||
|
'border-vega-pink dark:border-vega-pink': hasError,
|
||||||
|
},
|
||||||
|
className
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const inputStyle = ({
|
||||||
|
style,
|
||||||
|
disabled,
|
||||||
|
}: {
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
disabled?: boolean;
|
||||||
|
}) =>
|
||||||
|
disabled
|
||||||
|
? {
|
||||||
|
...style,
|
||||||
|
backgroundImage:
|
||||||
|
'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAAXNSR0IArs4c6QAAACNJREFUGFdjtLS0/M8ABcePH2eEsRlJl4BpBdHIuuFmEi0BABqjEQVjx/LTAAAAAElFTkSuQmCC)',
|
||||||
|
}
|
||||||
|
: style;
|
||||||
|
|
||||||
|
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||||
|
({ prependIconName, appendIconName, className, ...props }, ref) => {
|
||||||
|
className = `${className} h-28`;
|
||||||
|
if (prependIconName) {
|
||||||
|
className += ' pl-28';
|
||||||
|
}
|
||||||
|
if (appendIconName) {
|
||||||
|
className += ' pr-28';
|
||||||
|
}
|
||||||
|
const input = (
|
||||||
|
<input
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
className={classNames(inputClassNames({ className, ...props }))}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const iconName = prependIconName || appendIconName;
|
||||||
|
if (iconName !== undefined) {
|
||||||
|
const iconClassName = classNames(
|
||||||
|
['fill-black-60 dark:fill-white-60', 'absolute', 'z-10'],
|
||||||
|
{
|
||||||
|
'left-8': prependIconName,
|
||||||
|
'right-8': appendIconName,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const icon = <Icon name={iconName} className={iconClassName} size={16} />;
|
||||||
|
return (
|
||||||
|
<div className="inline-flex items-center relative">
|
||||||
|
{prependIconName && icon}
|
||||||
|
{input}
|
||||||
|
{appendIconName && icon}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
);
|
1
libs/ui-toolkit/src/components/select/index.ts
Normal file
1
libs/ui-toolkit/src/components/select/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './select';
|
10
libs/ui-toolkit/src/components/select/select.spec.tsx
Normal file
10
libs/ui-toolkit/src/components/select/select.spec.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { Select } from './select';
|
||||||
|
|
||||||
|
describe('Select', () => {
|
||||||
|
it('should render successfully', () => {
|
||||||
|
const { baseElement } = render(<Select />);
|
||||||
|
expect(baseElement).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
26
libs/ui-toolkit/src/components/select/select.stories.tsx
Normal file
26
libs/ui-toolkit/src/components/select/select.stories.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Story, Meta } from '@storybook/react';
|
||||||
|
import { Select } from './select';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
component: Select,
|
||||||
|
title: 'Select',
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
const Template: Story = (args) => (
|
||||||
|
<Select {...args}>
|
||||||
|
<option value="Only option">Only option</option>
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
Default.args = {};
|
||||||
|
|
||||||
|
export const WithError = Template.bind({});
|
||||||
|
WithError.args = {
|
||||||
|
hasError: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Disabled = Template.bind({});
|
||||||
|
Disabled.args = {
|
||||||
|
disabled: true,
|
||||||
|
};
|
20
libs/ui-toolkit/src/components/select/select.tsx
Normal file
20
libs/ui-toolkit/src/components/select/select.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { SelectHTMLAttributes, forwardRef } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { inputClassNames } from '../input/input';
|
||||||
|
|
||||||
|
export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
||||||
|
hasError?: boolean;
|
||||||
|
className?: string;
|
||||||
|
value?: string | number;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Select = forwardRef<HTMLSelectElement, SelectProps>(
|
||||||
|
(props, ref) => (
|
||||||
|
<select
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
className={classNames(inputClassNames(props), 'h-28')}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
1
libs/ui-toolkit/src/components/text-area/index.ts
Normal file
1
libs/ui-toolkit/src/components/text-area/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './text-area';
|
10
libs/ui-toolkit/src/components/text-area/text-area.spec.tsx
Normal file
10
libs/ui-toolkit/src/components/text-area/text-area.spec.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { TextArea } from './text-area';
|
||||||
|
|
||||||
|
describe('TextArea', () => {
|
||||||
|
it('should render successfully', () => {
|
||||||
|
const { baseElement } = render(<TextArea />);
|
||||||
|
expect(baseElement).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,26 @@
|
|||||||
|
import { Story, Meta } from '@storybook/react';
|
||||||
|
import { TextArea } from './text-area';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
component: TextArea,
|
||||||
|
title: 'TextArea',
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
const Template: Story = (args) => (
|
||||||
|
<TextArea {...args} className="h-48">
|
||||||
|
I type words
|
||||||
|
</TextArea>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
Default.args = {};
|
||||||
|
|
||||||
|
export const WithError = Template.bind({});
|
||||||
|
WithError.args = {
|
||||||
|
hasError: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Disabled = Template.bind({});
|
||||||
|
Disabled.args = {
|
||||||
|
disabled: true,
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user