Merge branch 'master' into task/token-flow-tests

This commit is contained in:
AndyWhiteVega 2022-06-24 10:25:36 +01:00
commit ef6b79e1f8
100 changed files with 1479 additions and 694 deletions
apps
libs
package.jsonyarn.lock

View File

@ -36,7 +36,12 @@ export const JumpTo = ({
placeholder={placeholder}
className="max-w-[200px]"
/>
<Button data-testid="go-submit" variant="secondary" type="submit">
<Button
data-testid="go-submit"
variant="secondary"
boxShadow={false}
type="submit"
>
{t('Go')}
</Button>
</div>

View File

@ -75,7 +75,12 @@ export const Search = () => {
</InputError>
)}
</FormGroup>
<Button type="submit" variant="secondary" data-testid="search-button">
<Button
type="submit"
boxShadow={false}
variant="secondary"
data-testid="search-button"
>
{t('Search')}
</Button>
</form>

View File

@ -35,7 +35,7 @@ export const DrawerToggle = ({
}, [variant]);
return (
<Button variant="inline" className={classes} onClick={onToggle}>
<Button variant="inline-link" className={classes} onClick={onToggle}>
<Icon name={iconName as IconName} />
</Button>
);

View File

@ -101,7 +101,7 @@ const SimpleMarketList = () => {
<div className="absolute right-16 top-1/2 -translate-y-1/2">
<Button
onClick={() => onClick(market.id)}
variant="inline"
variant="inline-link"
prependIconName="chevron-right"
/>
</div>

View File

@ -70,7 +70,7 @@ export default ({ steps }: StepperProps) => {
{index === steps.length - 1 ? 'Finish' : 'Continue'}
</Button>
<Button
variant="inline"
variant="inline-link"
disabled={index === 0}
onClick={handleBack}
>

View File

@ -38,7 +38,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "125550.67163847386329389",
"locked_amount": "125432.15784547661146416",
"deposits": [
{
"amount": "129999.45",
@ -488,7 +488,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "97499.58",
"total_removed": "0",
"locked_amount": "66375.714926165134519854",
"locked_amount": "66259.463935294023094392",
"deposits": [
{
"amount": "97499.58",
@ -521,7 +521,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "135173.4239508",
"total_removed": "0",
"locked_amount": "90724.1430149405949586301724",
"locked_amount": "90565.24798031609389317802476",
"deposits": [
{
"amount": "135173.4239508",
@ -554,7 +554,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "32499.86",
"total_removed": "0",
"locked_amount": "27923.117022151803344928",
"locked_amount": "27874.21223783955177767",
"deposits": [
{
"amount": "32499.86",
@ -587,7 +587,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "10833.29",
"total_removed": "0",
"locked_amount": "9088.703631196554874002",
"locked_amount": "9072.785598463712161582",
"deposits": [
{
"amount": "10833.29",
@ -675,7 +675,7 @@
"tranche_end": "2022-11-01T00:00:00.000Z",
"total_added": "22500",
"total_removed": "0",
"locked_amount": "15926.89509737318925",
"locked_amount": "15865.8047441123175",
"deposits": [
{
"amount": "15000",
@ -761,7 +761,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "0",
"locked_amount": "1824311.884226555593124554",
"locked_amount": "1821656.662619683475351818",
"deposits": [
{
"amount": "1852091.69",
@ -1777,7 +1777,7 @@
"tranche_end": "2022-09-30T00:00:00.000Z",
"total_added": "60916.66666633337",
"total_removed": "18323.723696937179372649",
"locked_amount": "15345.7844080554792796843344487108",
"locked_amount": "15267.7511993664485111836221926715",
"deposits": [
{
"amount": "2833.333333",
@ -5195,7 +5195,7 @@
"tranche_end": "2022-09-03T00:00:00.000Z",
"total_added": "19455.000000000000000003",
"total_removed": "5052.45813105178",
"locked_amount": "3797.54184693683445435058558856544901071",
"locked_amount": "3770.9133709094361165005814824010654489",
"deposits": [
{
"amount": "75",
@ -14079,7 +14079,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "74162.9780761646031",
"locked_amount": "2827833.53284815265111086312",
"locked_amount": "2823753.38168836704877442084",
"deposits": [
{
"amount": "1998.95815",
@ -14792,7 +14792,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15788853.065470999700000001",
"total_removed": "6529.308282907170975",
"locked_amount": "15248534.5651159366987063864185024232049202",
"locked_amount": "15234140.6821890069690086304937381947864288",
"deposits": [
{
"amount": "16249.93",
@ -16844,7 +16844,7 @@
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "2069544.949743920512285522",
"locked_amount": "8428346.7523447924808560906348842",
"locked_amount": "8414990.02955211055837547157567297",
"deposits": [
{
"amount": "129284.449",
@ -20892,7 +20892,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "1390546.591547348229906227",
"locked_amount": "3013187.37252800835890024298674749",
"locked_amount": "3007910.05057183280994625221544021",
"deposits": [
{
"amount": "552496.6455",
@ -22015,7 +22015,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "0",
"locked_amount": "448086.38786540236946778559309992",
"locked_amount": "447439.86459099593698764817757484",
"deposits": [
{
"amount": "3000",
@ -47668,7 +47668,7 @@
"tranche_start": "2021-12-05T00:00:00.000Z",
"tranche_end": "2022-06-05T00:00:00.000Z",
"total_added": "171288.42",
"total_removed": "29685.4825162206377",
"total_removed": "30935.4825162206377",
"locked_amount": "0",
"deposits": [
{
@ -51913,6 +51913,31 @@
"user": "0xcad0D46627628A8bAF524BF202669e8B9f04250f",
"tx": "0xa403d8af5a940c4abd4aadcc579e279218ae73b9c7fc3d7e4216d6aab5ba70eb"
},
{
"amount": "250",
"user": "0xEc925Fd72B46E2101D20686800343220c12Dedde",
"tx": "0xeaf8173b80d3ea3eb5c440a6edcc4fbf6d8624fa437203b66f6acb72d7462b5e"
},
{
"amount": "250",
"user": "0xbC10086CB362caE3071f2e09DF2aEE5265FdA238",
"tx": "0x1ea368a1c1f000d36d93627ccda127da12e4beade77d869aab7cadb7edfa354f"
},
{
"amount": "250",
"user": "0xDA83Ff6862ed0579248CEd14422C8B782732233b",
"tx": "0x7eb0ec3a5e3d8792b341881083f9046d550fdf0e92429b53bbdddfe439e83983"
},
{
"amount": "250",
"user": "0x2b28Fd98804e19cFe8d46f2d729C2A5195339fA4",
"tx": "0xd8c87e89222f0fea8933a403cd39756c6721d1e3d86a0bb7747b428f50c7f790"
},
{
"amount": "250",
"user": "0x0941FC05E3DA8d9dD29bCBB15F9FF3A16F342612",
"tx": "0xb3cffc52e62679835e65be5472f80bcfa949f7eb0c4c9948efc3271cca02fdae"
},
{
"amount": "183.6335597275",
"user": "0x690Fc36d52eD3f198F0eBDea1557333a1766f786",
@ -59380,10 +59405,17 @@
"tx": "0x96a22c369645e8945b4047374a05332f4054b50a492c43092e8bb8e974a006b9"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "250",
"user": "0xEc925Fd72B46E2101D20686800343220c12Dedde",
"tranche_id": 6,
"tx": "0xeaf8173b80d3ea3eb5c440a6edcc4fbf6d8624fa437203b66f6acb72d7462b5e"
}
],
"total_tokens": "250",
"withdrawn_tokens": "0",
"remaining_tokens": "250"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0x08e9b80fCa090CB3E50d77964dc8777cD828253D",
@ -59620,10 +59652,17 @@
"tx": "0x96a22c369645e8945b4047374a05332f4054b50a492c43092e8bb8e974a006b9"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "250",
"user": "0x2b28Fd98804e19cFe8d46f2d729C2A5195339fA4",
"tranche_id": 6,
"tx": "0xd8c87e89222f0fea8933a403cd39756c6721d1e3d86a0bb7747b428f50c7f790"
}
],
"total_tokens": "250",
"withdrawn_tokens": "0",
"remaining_tokens": "250"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0x61f908D9ee13a68983e5FECc8D9467232F5E9cB4",
@ -59680,10 +59719,17 @@
"tx": "0x96a22c369645e8945b4047374a05332f4054b50a492c43092e8bb8e974a006b9"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "250",
"user": "0xDA83Ff6862ed0579248CEd14422C8B782732233b",
"tranche_id": 6,
"tx": "0x7eb0ec3a5e3d8792b341881083f9046d550fdf0e92429b53bbdddfe439e83983"
}
],
"total_tokens": "250",
"withdrawn_tokens": "0",
"remaining_tokens": "250"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0xbC10086CB362caE3071f2e09DF2aEE5265FdA238",
@ -59695,10 +59741,17 @@
"tx": "0x96a22c369645e8945b4047374a05332f4054b50a492c43092e8bb8e974a006b9"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "250",
"user": "0xbC10086CB362caE3071f2e09DF2aEE5265FdA238",
"tranche_id": 6,
"tx": "0x1ea368a1c1f000d36d93627ccda127da12e4beade77d869aab7cadb7edfa354f"
}
],
"total_tokens": "250",
"withdrawn_tokens": "0",
"remaining_tokens": "250"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0xEe3183EcE9ee7d73Fb7bA7F4eB262A2dE68C42B0",
@ -62281,10 +62334,17 @@
"tx": "0x9f916cf09e8a3c4ade0ffce5190db464d0a2b1dadba78e1ee7ba5d6e751d6148"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "250",
"user": "0x0941FC05E3DA8d9dD29bCBB15F9FF3A16F342612",
"tranche_id": 6,
"tx": "0xb3cffc52e62679835e65be5472f80bcfa949f7eb0c4c9948efc3271cca02fdae"
}
],
"total_tokens": "250",
"withdrawn_tokens": "0",
"remaining_tokens": "250"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0x0a5688ed5b0b804db2ee18767476bb60598EC398",

View File

@ -38,7 +38,7 @@
"tranche_end": "2022-11-26T13:48:10.000Z",
"total_added": "100",
"total_removed": "0",
"locked_amount": "42.69090246067986",
"locked_amount": "42.554014459665146",
"deposits": [
{
"amount": "100",
@ -242,7 +242,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1100",
"total_removed": "673.04388635",
"locked_amount": "332.36188165905626",
"locked_amount": "330.85611364789445",
"deposits": [
{
"amount": "1000",

View File

@ -69,7 +69,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1010.000000000000000001",
"total_removed": "668.4622323651",
"locked_amount": "305.16870084982244640030214722856418064",
"locked_amount": "303.7861000126839150003007783168442415",
"deposits": [
{
"amount": "1000",

View File

@ -1,29 +1,17 @@
# 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
REACT_APP_VERSION=$npm_package_version
REACT_APP_REPOSITORY_URL=$REPOSITORY_URL
REACT_APP_BRANCH=$BRANCH
REACT_APP_PULL_REQUEST=$PULL_REQUEST
REACT_APP_HEAD=$HEAD
REACT_APP_COMMIT_REF=$COMMIT_REF
REACT_APP_CONTEXT=$CONTEXT
REACT_APP_REVIEW_ID=$REVIEW_ID
REACT_APP_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
REACT_APP_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
REACT_APP_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
REACT_APP_URL=$URL
REACT_APP_DEPLOY_URL=$DEPLOY_URL
REACT_APP_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
# App configuration variables
NX_VEGA_ENV=TESTNET
NX_VEGA_URL=https://lb.testnet.vega.xyz/query
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_FAIRGROUND=false
NX_IS_NEW_BRIDGE_CONTRACT=true
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.token.vega.xyz","STAGNET2":"staging2.token.vega.xyz","TESTNET":"token.fairground.wtf","MAINNET":"token.vega.xyz"}'
NX_VEGA_URL=http://localhost:3028/query
NX_VEGA_REST=http://localhost:3029
NX_ETHEREUM_CHAIN_ID=1440
NX_ETH_URL_CONNECT=1
NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
#Test configuration variables
CYPRESS_FAIRGROUND=false

View File

@ -1,23 +1,3 @@
# 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
REACT_APP_VERSION=$npm_package_version
REACT_APP_REPOSITORY_URL=$REPOSITORY_URL
REACT_APP_BRANCH=$BRANCH
REACT_APP_PULL_REQUEST=$PULL_REQUEST
REACT_APP_HEAD=$HEAD
REACT_APP_COMMIT_REF=$COMMIT_REF
REACT_APP_CONTEXT=$CONTEXT
REACT_APP_REVIEW_ID=$REVIEW_ID
REACT_APP_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
REACT_APP_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
REACT_APP_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
REACT_APP_URL=$URL
REACT_APP_DEPLOY_URL=$DEPLOY_URL
REACT_APP_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
# App configuration variables
NX_VEGA_ENV=TESTNET
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json

17
apps/token/.env.capsule Normal file
View File

@ -0,0 +1,17 @@
# App configuration variables
NX_VEGA_ENV=TESTNET
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_FAIRGROUND=false
NX_IS_NEW_BRIDGE_CONTRACT=true
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.token.vega.xyz","STAGNET2":"staging2.token.vega.xyz","TESTNET":"token.fairground.wtf","MAINNET":"token.vega.xyz"}'
NX_VEGA_URL=http://localhost:3028/query
NX_VEGA_REST=http://localhost:3029
NX_ETHEREUM_CHAIN_ID=1440
NX_ETH_URL_CONNECT=1
NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
#Test configuration variables
CYPRESS_FAIRGROUND=false

View File

@ -38,23 +38,19 @@ yarn nx run token:serve --env={env} # e.g. stagnet1
There are a few different configuration options offered for this app:
| **Flag** | **Purpose** |
| ------------------------------ | ---------------------------------------------------------------------------------------------------- |
| `NX_APP_SENTRY_DSN` | The sentry endpoint to report to. Should be off in dev but set in live. |
| `NX_APP_CHAIN` | The ETH chain for the app to work on. Should be mainnet for live, but ropsten for preview deploys. |
| `NX_APP_VEGA_URL` | The GraphQL query endpoint of a [Vega data node](https://github.com/vegaprotocol/networks#data-node) |
| `NX_APP_DEX_STAKING_DISABLED` | Disable the dex liquidity page an show a coming soon message |
| `NX_APP_FAIRGROUND` | Change styling to be themed as the fairground version of the website |
| `NX_APP_INFURA_ID` | Infura fallback for if the user does not have a web3 compatible browser |
| `NX_APP_HOSTED_WALLET_ENABLED` | If the hosted wallet is enabled or not. If so then allow users to login using the hosted wallet |
| `NX_APP_ENV` | Change network to connect to. When set to CUSTOM use CUSTOM\_\* vars for network parameters |
| `NX_CUSTOM_URLS` | When NX_APP_ENV=CUSTOM use these Data Node REST URLs, optional if CUSTOM_URLS_WITH_GRAPHQL is used. |
| `NX_CUSTOM_URLS_WITH_GRAPHQL` | When NX_APP_ENV=CUSTOM use these Data Node GraphQL URLs, optional if CUSTOM_URLS is used. |
| `NX_CUSTOM_TOKEN_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega token address. |
| `NX_CUSTOM_CLAIM_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega claim address. |
| `NX_CUSTOM_LOCKED_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega locked address. |
| `NX_CUSTOM_VESTING_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega vesting address. |
| `NX_CUSTOM_STAKING_BRIDGE` | When NX_APP_ENV=CUSTOM specify Vega staking bridge address. |
| **Flag** | **Purpose** |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NX_APP_SENTRY_DSN` | The sentry endpoint to report to. Should be off in dev but set in live. |
| `NX_APP_CHAIN` | The ETH chain for the app to work on. Should be mainnet for live, but ropsten for preview deploys. |
| `NX_APP_VEGA_URL` | The GraphQL query endpoint of a [Vega data node](https://github.com/vegaprotocol/networks#data-node) |
| `NX_APP_DEX_STAKING_DISABLED` | Disable the dex liquidity page an show a coming soon message |
| `NX_APP_FAIRGROUND` | Change styling to be themed as the fairground version of the website |
| `NX_APP_INFURA_ID` | Infura fallback for if the user does not have a web3 compatible browser |
| `NX_APP_HOSTED_WALLET_ENABLED` | If the hosted wallet is enabled or not. If so then allow users to login using the hosted wallet |
| `NX_APP_ENV` | Change network to connect to. When set to CUSTOM use CUSTOM\_\* vars for network parameters |
| `NX_ETH_URL_CONNECT` (optional) | If set to true the below two must also be set. This allows siging transactions in brower to allow to connect to a local ganache node through cypress |
| `NX_ETH_WALLET_MNEMONIC` (optional) | The mnemonic to be used to sign transactions with in browser |
| `NX_LOCAL_PROVIDER_URL` (optional) | The local node to use to send transaction to when signing in browser |
## Example configs:

View File

@ -2,6 +2,7 @@ import { useWeb3React } from '@web3-react/core';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { getButtonClasses, Button } from '@vegaprotocol/ui-toolkit';
import {
AppStateActionType,
@ -70,7 +71,7 @@ const AssociatedAmounts = ({
rightLabel={t('notAssociated')}
leftColor={Colors.white.DEFAULT}
rightColor={Colors.black.DEFAULT}
light={true}
light={false}
/>
{vestingAssociationByVegaKey.length ? (
<>
@ -129,6 +130,7 @@ const ConnectedKey = () => {
name="VEGA"
symbol="In vesting contract"
balance={totalInVestingContract}
dark={true}
/>
<LockedProgress
locked={totalLockedBalance}
@ -136,7 +138,7 @@ const ConnectedKey = () => {
total={totalVestedBalance.plus(totalLockedBalance)}
leftLabel={t('Locked')}
rightLabel={t('Unlocked')}
light={true}
light={false}
/>
</>
)}
@ -153,6 +155,7 @@ const ConnectedKey = () => {
name="VEGA"
symbol="In Wallet"
balance={walletWithAssociations}
dark={true}
/>
{!Object.keys(
appState.associationBreakdown.stakingAssociations
@ -163,15 +166,17 @@ const ConnectedKey = () => {
/>
)}
<WalletCardActions>
<Link className="flex-1" to={`${Routes.STAKING}/associate`}>
<span className="flex items-center justify-center w-full text-center px-28 border h-28">
{t('associate')}
</span>
<Link
className={getButtonClasses('flex-1 mr-4', 'secondary')}
to={`${Routes.STAKING}/associate`}
>
{t('associate')}
</Link>
<Link className="flex-1" to={`${Routes.STAKING}/disassociate`}>
<span className="flex items-center justify-center w-full px-28 border h-28">
{t('disassociate')}
</span>
<Link
className={getButtonClasses('flex-1 ml-4', 'secondary')}
to={`${Routes.STAKING}/disassociate`}
>
{t('disassociate')}
</Link>
</WalletCardActions>
</>
@ -185,7 +190,7 @@ export const EthWallet = () => {
const pendingTxs = usePendingTransactions();
return (
<WalletCard>
<WalletCard dark={true}>
<WalletCardHeader>
<h1 className="text-h3 uppercase">{t('ethereumKey')}</h1>
{account && (
@ -203,7 +208,7 @@ export const EthWallet = () => {
})
}
>
<Loader size="small" forceTheme="light" />
<Loader size="small" forceTheme="dark" />
{t('pendingTransactions')}
</button>
</div>
@ -215,7 +220,8 @@ export const EthWallet = () => {
{account ? (
<ConnectedKey />
) : (
<button
<Button
variant={'secondary'}
className="w-full px-28 border h-28"
onClick={() =>
appDispatch({
@ -226,7 +232,7 @@ export const EthWallet = () => {
data-test-id="connect-to-eth-wallet-button"
>
{t('connectEthWalletToAssociate')}
</button>
</Button>
)}
{account && (
<WalletCardActions>

View File

@ -1,8 +1,8 @@
import React from 'react';
import { formatNumber } from '../../lib/format-number';
import type { BigNumber } from '../../lib/bignumber';
import { theme } from '@vegaprotocol/tailwindcss-config';
import classnames from 'classnames';
const Colors = theme.colors;
@ -14,9 +14,10 @@ const ProgressContents = ({
children: React.ReactNode;
}) => (
<div
className={`flex justify-between py-2 font-mono ${
light ? 'gap-0 px-0 text-black' : 'gap-y-0 gap-x-4 px-4'
}`}
className={classnames('flex justify-between py-2 font-mono', {
'gap-0 px-0 text-black': light,
'gap-y-0 gap-x-4': !light,
})}
>
{children}
</div>
@ -25,17 +26,22 @@ const ProgressContents = ({
const ProgressIndicator = ({
bgColor,
side,
light,
}: {
bgColor: string;
side: 'left' | 'right';
light: boolean;
}) => (
<span
style={{
backgroundColor: bgColor,
}}
className={`inline-block w-12 h-12 border border-black ${
side === 'left' ? 'mr-8' : 'ml-8'
}`}
className={classnames('inline-block w-12 h-12 border', {
'mr-8': side === 'left',
'ml-8': side === 'right',
'border-black': light,
'border-white': !light,
})}
/>
);
@ -64,6 +70,7 @@ export interface LockedProgressProps {
leftColor?: string;
rightColor?: string;
light?: boolean;
decimals?: number;
}
export const LockedProgress = ({
@ -73,8 +80,9 @@ export const LockedProgress = ({
leftLabel,
rightLabel,
leftColor = Colors.vega.pink,
rightColor = Colors.green.DEFAULT,
rightColor = Colors.vega.green,
light = false,
decimals = 2,
}: LockedProgressProps) => {
const lockedPercentage = React.useMemo(() => {
return locked.div(total).times(100);
@ -85,26 +93,35 @@ export const LockedProgress = ({
}, [total, unlocked]);
return (
<div className="border-x border-x-white">
<div className={`flex ${light && 'border border-black'}`}>
<>
<div
className={classnames('flex border', {
'border-black': light,
'border-white': !light,
})}
>
<ProgressBar percentage={lockedPercentage} bgColor={leftColor} />
<ProgressBar percentage={unlockedPercentage} bgColor={rightColor} />
</div>
<ProgressContents light={light}>
<span>
<ProgressIndicator bgColor={leftColor} side={'left'} />
<ProgressIndicator bgColor={leftColor} side={'left'} light={false} />
{leftLabel}
</span>
<span>
{rightLabel}
<ProgressIndicator bgColor={rightColor} side={'right'} />
<ProgressIndicator
bgColor={rightColor}
side={'right'}
light={false}
/>
</span>
</ProgressContents>
<ProgressContents light={light}>
<span>{formatNumber(locked, 2)}</span>
<span>{formatNumber(unlocked, 2)}</span>
<span>{formatNumber(locked, decimals)}</span>
<span>{formatNumber(unlocked, decimals)}</span>
</ProgressContents>
</div>
</>
);
};

View File

@ -180,15 +180,15 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
</div>
))}
<WalletCardActions>
<Link style={{ flex: 1 }} to={Routes.GOVERNANCE}>
<span className="flex items-center justify-center w-full px-28 border h-28 bg-white text-black">
<Link className="flex-1 pr-8" to={Routes.GOVERNANCE}>
<Button variant={'secondary'} className="w-full">
{t('governance')}
</span>
</Button>
</Link>
<Link style={{ flex: 1 }} to={Routes.STAKING}>
<span className="flex items-center justify-center w-full px-28 border h-28 bg-white text-black">
<Link className="flex-1 pl-8" to={Routes.STAKING}>
<Button variant={'secondary'} className="w-full">
{t('staking')}
</span>
</Button>
</Link>
</WalletCardActions>
<VegaWalletAssetList accounts={accounts} />

View File

@ -55,6 +55,9 @@ export const ENV = {
commit: windowOrDefault('NX_COMMIT_REF'),
branch: windowOrDefault('NX_BRANCH'),
vegaUrl: windowOrDefault('NX_VEGA_URL'),
urlConnect: TRUTHY.includes(windowOrDefault('NX_ETH_URL_CONNECT')),
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
flags: {
NETWORK_DOWN: TRUTHY.includes(windowOrDefault('NX_NETWORK_DOWN')),
HOSTED_WALLET_ENABLED: TRUTHY.includes(

View File

@ -1,10 +1,8 @@
import React from 'react';
import { useContracts } from '../../contexts/contracts/contracts-context';
import mock from './tranches-mock';
import type { Tranche } from '@vegaprotocol/smart-contracts';
export function useTranches() {
const { vesting } = useContracts();
const [tranches, setTranches] = React.useState<Tranche[] | null>(null);
const [error, setError] = React.useState<string | null>(null);
@ -17,7 +15,7 @@ export function useTranches() {
}
};
run();
}, [vesting]);
}, []);
return { tranches, error };
}

View File

@ -1,3 +1,4 @@
import { toBigNum } from '@vegaprotocol/react-helpers';
import React from 'react';
import {
@ -7,23 +8,26 @@ import {
import { useContracts } from '../contexts/contracts/contracts-context';
export function useRefreshAssociatedBalances() {
const { appDispatch } = useAppState();
const {
appDispatch,
appState: { decimals },
} = useAppState();
const { staking, vesting } = useContracts();
return React.useCallback(
async (ethAddress: string, vegaKey: string) => {
const [walletAssociatedBalance, vestingAssociatedBalance] =
await Promise.all([
staking.stakeBalance(ethAddress, `0x${vegaKey}`),
vesting.stakeBalance(ethAddress, `0x${vegaKey}`),
staking.stakeBalance(ethAddress, vegaKey),
vesting.stakeBalance(ethAddress, vegaKey),
]);
appDispatch({
type: AppStateActionType.REFRESH_ASSOCIATED_BALANCES,
walletAssociatedBalance,
vestingAssociatedBalance,
walletAssociatedBalance: toBigNum(walletAssociatedBalance, decimals),
vestingAssociatedBalance: toBigNum(vestingAssociatedBalance, decimals),
});
},
[staking, vesting, appDispatch]
[staking, vesting, appDispatch, decimals]
);
}

View File

@ -193,6 +193,7 @@
"noGovernanceTokens": "You need some VEGA tokens to participate in governance",
"youVoted": "You voted",
"changeVote": "Change vote",
"voteRequested": "Please confirm transaction in wallet",
"votePending": "Casting vote",
"voteError": "Something went wrong, and your vote was not seen by the network",
"back": "back",
@ -280,6 +281,7 @@
"stakingHasAssociated": "You have {{tokens}} $VEGA tokens associated.",
"stakingAssociateMoreButton": "Associate more $VEGA tokens with wallet",
"stakingDisassociateButton": "Disassociate $VEGA tokens from wallet",
"stakingConfirm": "Open you wallet app to confirm",
"associateButton": "Associate $VEGA tokens with wallet",
"nodeQueryFailed": "Could not get data for validator {{node}}",
"stakeAddPendingTitle": "Adding {{amount}} $VEGA to validator {{node}}",
@ -540,5 +542,8 @@
"numberOfAgainstVotes": "Number of votes against",
"yesPercentage": "Yes percentage",
"noPercentage": "No percentage",
"proposalTerms": "Proposal terms"
"proposalTerms": "Proposal terms",
"currentlySetTo": "Currently set to ",
"pass": "Pass",
"fail": "Fail"
}

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en" class="bg-black w-full h-full">
<html lang="en" class="dark bg-black w-full h-full">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="//static.vega.xyz/fonts.css" />

View File

@ -0,0 +1,150 @@
import { ethers, Wallet } from 'ethers';
import { Connector } from '@web3-react/types';
import { Eip1193Bridge } from '@ethersproject/experimental';
import type { ConnectionInfo } from '@ethersproject/web';
import type { Actions } from '@web3-react/types';
import { ENV } from '../config/env';
export class CustomizedBridge extends Eip1193Bridge {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async sendAsync(...args: any) {
console.debug('sendAsync called', ...args);
return this.send(...args);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
override async send(...args: any) {
console.debug('send called', ...args);
const isCallbackForm =
typeof args[0] === 'object' && typeof args[1] === 'function';
let callback;
let method;
let params;
if (isCallbackForm) {
callback = args[1];
method = args[0].method;
params = args[0].params;
} else {
method = args[0];
params = args[1];
}
try {
// Hacky, https://github.com/ethers-io/ethers.js/issues/1683#issuecomment-1016227588
// If from is present on eth_call it errors, removing it makes the library set
// from as the connected wallet which works fine
if (params && params.length && params[0].from && method === 'eth_call')
delete params[0].from;
let result;
// For sending a transaction if we call send it will error
// as it wants gasLimit in sendTransaction but hexlify sets the property gas
// to gasLimit which makes sensd transaction error.
// This has taken the code from the super method for sendTransaction and altered
// it slightly to make it work with the gas limit issues.
if (
params &&
params.length &&
params[0].from &&
method === 'eth_sendTransaction'
) {
// Hexlify will not take gas, must be gasLimit, set this property to be gasLimit
params[0].gasLimit = params[0].gas;
delete params[0].gas;
// If from is present on eth_sendTransaction it errors, removing it makes the library set
// from as the connected wallet which works fine
delete params[0].from;
const req = ethers.providers.JsonRpcProvider.hexlifyTransaction(
params[0]
);
// Hexlify sets the gasLimit property to be gas again and send transaction requires gasLimit
req['gasLimit'] = req['gas'];
delete req['gas'];
if (!this.signer) {
throw new Error('No signer');
}
// Send the transaction
const tx = await this.signer.sendTransaction(req);
result = tx.hash;
} else {
// All other transactions the base class works for
result = await super.send(method, params);
}
console.debug('result received', method, params, result);
if (isCallbackForm) {
callback(null, { result });
} else {
return result;
}
} catch (error) {
console.error(error);
if (isCallbackForm) {
callback(error, null);
} else {
throw error;
}
}
}
}
type url = string | ConnectionInfo;
export class Url extends Connector {
/** {@inheritdoc Connector.provider} */
public override provider: Eip1193Bridge | undefined;
private eagerConnection?: Promise<void>;
private url: url;
/**
* @param url - An RPC url.
* @param connectEagerly - A flag indicating whether connection should be initiated when the class is constructed.
*/
constructor(actions: Actions, url: url, connectEagerly = false) {
super(actions);
if (connectEagerly && typeof window === 'undefined') {
throw new Error(
'connectEagerly = true is invalid for SSR, instead use the activate method in a useEffect'
);
}
this.url = url;
if (connectEagerly) void this.activate();
}
private async isomorphicInitialize() {
if (this.eagerConnection) return this.eagerConnection;
await (this.eagerConnection = import('@ethersproject/providers')
.then(({ JsonRpcProvider }) => JsonRpcProvider)
.then((JsonRpcProvider) => {
const provider = new JsonRpcProvider(this.url);
const privateKey = Wallet.fromMnemonic(
ENV.ethWalletMnemonic,
`m/44'/60'/0'/0/0`
).privateKey;
const signer = new Wallet(privateKey, provider);
this.provider = new CustomizedBridge(signer, provider);
this.actions.update({ accounts: [signer.address], chainId: 1440 });
}));
}
/** {@inheritdoc Connector.activate} */
public async activate(): Promise<void> {
this.actions.startActivation();
await this.isomorphicInitialize();
try {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const chainId = await this.provider!.request({ method: 'eth_chainId' });
this.actions.update({ chainId: Number(chainId) });
} catch (error) {
this.actions.reportError(error as Error);
}
}
}

View File

@ -3,11 +3,18 @@ import type { Web3ReactHooks } from '@web3-react/core';
import { initializeConnector } from '@web3-react/core';
import { MetaMask } from '@web3-react/metamask';
import { WalletConnect } from '@web3-react/walletconnect';
import { Url } from './url-connector';
import type { Connector } from '@web3-react/types';
import { ENV } from '../config/env';
const [metamask, metamaskHooks] = initializeConnector<MetaMask>(
(actions) => new MetaMask(actions)
);
const [urlConnector, urlHooks] = initializeConnector<Url>(
(actions) => new Url(actions, ENV.localProviderUrl)
);
export const createDefaultProvider = (providerUrl: string, chainId: number) => {
return new ethers.providers.JsonRpcProvider(providerUrl, chainId);
};
@ -27,7 +34,8 @@ export const createConnectors = (providerUrl: string, chainId: number) => {
[chainId]
);
return [
ENV.urlConnect ? [urlConnector, urlHooks] : null,
[metamask, metamaskHooks],
[walletconnect, walletconnectHooks],
] as [MetaMask | WalletConnect, Web3ReactHooks][];
].filter(Boolean) as [Connector, Web3ReactHooks][];
};

View File

@ -25,7 +25,7 @@ export const TargetAddressMismatch = ({
}}
components={{
bold: <strong />,
red: <span className={'text-red'} />,
red: <span className={'text-vega-red'} />,
}}
/>
</p>

View File

@ -43,7 +43,7 @@ const Contracts = () => {
title={t('View address on Etherscan')}
href={`${ETHERSCAN_URL}/address/${contract.address}`}
>
{config.collateral_bridge_contract.address}
{contract.address}
</Link>
</div>
);
@ -58,7 +58,6 @@ const Contracts = () => {
title={t('View address on Etherscan')}
href={`${ETHERSCAN_URL}/address/${value}`}
>
asdfasd
{value}
</Link>
</div>

View File

@ -34,30 +34,22 @@ export const CurrentProposalStatus = ({
{ addSuffix: true }
);
if (proposal.state === ProposalState.Open && willPass) {
return <StatusPass>{t('shouldPass')}</StatusPass>;
}
if (!participationMet) {
return (
<>
<span>{t('voteFailedReason')}</span>
<span className="current-proposal-status__fail">
{t('participationNotMet')}
</span>
<span>&nbsp;{daysClosedAgo}.</span>
</>
);
}
if (!majorityMet) {
return (
<>
<span>{t('voteFailedReason')}</span>
<StatusFail>{t('majorityNotMet')}</StatusFail>
<span>&nbsp;{daysClosedAgo}.</span>
</>
);
if (proposal.state === ProposalState.Open) {
if (willPass) {
return (
<>
{t('currentlySetTo')}
<StatusPass>{t('pass')}</StatusPass>
</>
);
} else {
return (
<>
{t('currentlySetTo')}
<StatusFail>{t('fail')}</StatusFail>
</>
);
}
}
if (
@ -65,6 +57,26 @@ export const CurrentProposalStatus = ({
proposal.state === ProposalState.Declined ||
proposal.state === ProposalState.Rejected
) {
if (!participationMet) {
return (
<>
<span>{t('voteFailedReason')}</span>
<StatusFail>{t('participationNotMet')}</StatusFail>
<span>&nbsp;{daysClosedAgo}.</span>
</>
);
}
if (!majorityMet) {
return (
<>
<span>{t('voteFailedReason')}</span>
<StatusFail>{t('majorityNotMet')}</StatusFail>
<span>&nbsp;{daysClosedAgo}.</span>
</>
);
}
return (
<>
<span>{t('voteFailedReason')}</span>

View File

@ -17,6 +17,7 @@ export enum VoteState {
NotCast = 'NotCast',
Yes = 'Yes',
No = 'No',
Requested = 'Requested',
Pending = 'Pending',
Failed = 'Failed',
}
@ -69,7 +70,7 @@ export function useUserVote(
}
}, [userVote]);
// Start a starts a timeout of 30s to set a failed message if
// Starts a timeout of 30s to set a failed message if
// the vote is not seen by the time the callback is invoked
React.useEffect(() => {
// eslint-disable-next-line
@ -93,7 +94,7 @@ export function useUserVote(
async function castVote(value: VoteValue) {
if (!proposalId || !keypair) return;
setVoteState(VoteState.Pending);
setVoteState(VoteState.Requested);
try {
const variables = {
@ -105,6 +106,7 @@ export function useUserVote(
},
};
await sendTx(variables);
setVoteState(VoteState.Pending);
// Now await vote via poll in parent component
} catch (err) {

View File

@ -124,6 +124,10 @@ export const VoteButtons = ({
return <p>{cantVoteUI}</p>;
}
if (voteState === VoteState.Requested) {
return <p>{t('voteRequested')}...</p>;
}
if (voteState === VoteState.Pending) {
return <p>{t('votePending')}...</p>;
}

View File

@ -49,7 +49,7 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
<span>
<CurrentProposalStatus proposal={proposal} />
</span>
.&nbsp;
{'. '}
{proposal.state === ProposalState.Open ? daysLeft : null}
</p>
<table className="w-full font-normal mb-12">

View File

@ -8,7 +8,6 @@ import { Link } from 'react-router-dom';
import { EthConnectPrompt } from '../../components/eth-connect-prompt';
import { SplashLoader } from '../../components/splash-loader';
import { useAppState } from '../../contexts/app-state/app-state-context';
import { useContracts } from '../../contexts/contracts/contracts-context';
import { useTranches } from '../../hooks/use-tranches';
import { Routes as RoutesConfig } from '../router-config';
import {
@ -19,7 +18,6 @@ import {
const RedemptionRouter = () => {
const { t } = useTranslation();
const { vesting } = useContracts();
const [state, dispatch] = React.useReducer(
redemptionReducer,
initialRedemptionState
@ -49,7 +47,7 @@ const RedemptionRouter = () => {
if (account) {
run(account);
}
}, [account, tranches, vesting]);
}, [account, tranches]);
if (error) {
return (

View File

@ -76,6 +76,7 @@ export const TrancheItem = ({
total={total}
leftLabel={t('Locked')}
rightLabel={t('Unlocked')}
decimals={18}
/>
<div className="text-right" data-testid="tranche-item-footer">

View File

@ -12,7 +12,7 @@ import { useTransaction } from '../../../hooks/use-transaction';
import { BigNumber } from '../../../lib/bignumber';
import { AssociateInfo } from './associate-info';
import type { VegaKeyExtended } from '@vegaprotocol/wallet';
import { toBigNum } from '@vegaprotocol/react-helpers';
import { removeDecimal, toBigNum } from '@vegaprotocol/react-helpers';
import type { EthereumConfig } from '@vegaprotocol/web3';
export const WalletAssociate = ({
@ -35,19 +35,18 @@ export const WalletAssociate = ({
appDispatch,
appState: { walletBalance, allowance, walletAssociatedBalance, decimals },
} = useAppState();
const { token } = useContracts();
const {
state: approveState,
perform: approve,
dispatch: approveDispatch,
} = useTransaction(() =>
token.approve(
} = useTransaction(() => {
return token.approve(
ethereumConfig.staking_bridge_contract.address,
Number.MAX_SAFE_INTEGER.toString()
)
);
removeDecimal('1000000', decimals).toString()
);
});
// Once they have approved deposits then we need to refresh their allowance
React.useEffect(() => {

View File

@ -8,6 +8,7 @@ import {
} from '../../../components/staking-method-radio';
import { TxState } from '../../../hooks/transaction-reducer';
import { useSearchParams } from '../../../hooks/use-search-params';
import { useRefreshAssociatedBalances } from '../../../hooks/use-refresh-associated-balances';
import { ContractDisassociate } from './contract-disassociate';
import { DisassociateTransaction } from './disassociate-transaction';
import { useRemoveStake } from './hooks';
@ -26,6 +27,7 @@ export const DisassociatePage = ({
const [amount, setAmount] = React.useState<string>('');
const [selectedStakingMethod, setSelectedStakingMethod] =
React.useState<StakingMethod | null>(params.method || null);
const refreshBalances = useRefreshAssociatedBalances();
// Clear the amount when the staking method changes
React.useEffect(() => {
@ -38,6 +40,12 @@ export const DisassociatePage = ({
perform: txPerform,
} = useRemoveStake(address, amount, vegaKey.pub, selectedStakingMethod);
React.useEffect(() => {
if (txState.txState === TxState.Complete) {
refreshBalances(address, vegaKey.pub);
}
}, [txState, refreshBalances, address, vegaKey.pub]);
if (txState.txState !== TxState.Default) {
return (
<DisassociateTransaction

View File

@ -18,7 +18,14 @@ import type {
import { StakeFailure } from './stake-failure';
import { StakePending } from './stake-pending';
import { StakeSuccess } from './stake-success';
import { Button, FormGroup, RadioGroup, Radio } from '@vegaprotocol/ui-toolkit';
import {
Button,
Callout,
FormGroup,
Intent,
Radio,
RadioGroup,
} from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import type {
DelegateSubmissionBody,
@ -46,6 +53,7 @@ export const PARTY_DELEGATIONS_QUERY = gql`
enum FormState {
Default,
Requested,
Pending,
Success,
Failure,
@ -115,7 +123,7 @@ export const StakingForm = ({
}, [action, availableStakeToAdd, availableStakeToRemove]);
async function onSubmit() {
setFormState(FormState.Pending);
setFormState(FormState.Requested);
const delegateInput: DelegateSubmissionBody = {
pubKey: pubkey,
propagate: true,
@ -139,6 +147,7 @@ export const StakingForm = ({
try {
const command = action === Actions.Add ? delegateInput : undelegateInput;
await sendTx(command);
setFormState(FormState.Pending);
// await success via poll
} catch (err) {
@ -184,6 +193,12 @@ export const StakingForm = ({
if (formState === FormState.Failure) {
return <StakeFailure nodeName={nodeName} />;
} else if (formState === FormState.Requested) {
return (
<Callout title="Confirm transaction in wallet" intent={Intent.Warning}>
<p>{t('stakingConfirm')}</p>
</Callout>
);
} else if (formState === FormState.Pending) {
return <StakePending action={action} amount={amount} nodeName={nodeName} />;
} else if (formState === FormState.Success) {

View File

@ -88,7 +88,9 @@ export const StakingNode = ({ vegaKey, data }: StakingNodeProps) => {
if (!nodeInfo) {
return (
<span className={'text-red'}>{t('stakingNodeNotFound', { node })}</span>
<span className={'text-vega-red'}>
{t('stakingNodeNotFound', { node })}
</span>
);
}

View File

@ -40,7 +40,7 @@ export const TrancheProgress = ({
<span className="tranches__progress-title">{t('Redeemed')}</span>
<ProgressBar
width={220}
color={Colors.green.DEFAULT}
color={Colors.vega.green}
percentage={removedPercentage}
/>
<span className="tranches__progress-numbers">

View File

@ -36,7 +36,7 @@ export const VestingChart = () => {
['pink', Colors.vega.pink],
['green', Colors.vega.green],
['orange', Colors.orange],
['yellow', Colors.yellow.DEFAULT],
['yellow', Colors.vega.yellow],
].map(([key, color]) => (
<linearGradient key={key} id={key} x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={color} stopOpacity={0.85} />
@ -119,7 +119,7 @@ export const VestingChart = () => {
dot={false}
type="monotone"
dataKey="publicSale"
stroke={Colors.yellow.DEFAULT}
stroke={Colors.vega.yellow}
fill="url(#yellow)"
yAxisId={0}
strokeWidth={2}

View File

@ -76,6 +76,7 @@ export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
id: 'c6f4337b31ed57a961969c3ba10297b369d01b9e75a4cbb96db4fc62886444e6',
name: 'BTCUSD Monthly (30 Jun 2022)',
decimalPlaces: 5,
positionDecimalPlaces: 0,
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {

View File

@ -1,8 +1,8 @@
import { useRouter } from 'next/router';
import { Vega } from '../icons/vega';
import Link from 'next/link';
import { AnchorButton } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers';
import classNames from 'classnames';
export const Navbar = () => {
return (
@ -33,14 +33,17 @@ const NavLink = ({ name, path, exact, testId = name }: NavLinkProps) => {
const router = useRouter();
const isActive =
router.asPath === path || (!exact && router.asPath.startsWith(path));
const linkClasses = classNames(
'px-16 py-6 border-0 self-end',
'uppercase xs:text-ui sm:text-body-large md:text-h5 lg:text-h4',
{
'bg-vega-pink dark:bg-vega-yellow text-white dark:text-black': isActive,
'text-black dark:text-white': !isActive,
}
);
return (
<AnchorButton
variant={isActive ? 'accent' : 'inline'}
className="px-16 py-6 h-[38px] uppercase border-0 self-end xs:text-ui sm:text-body-large md:text-h5 lg:text-h4"
data-testid={testId}
href={path}
>
{name}
</AnchorButton>
<Link data-testid={testId} href={path} passHref={true}>
<a className={linkClasses}>{name}</a>
</Link>
);
};

View File

@ -24,84 +24,80 @@ function AppBody({ Component, pageProps }: AppProps) {
const { push } = useRouter();
const store = useGlobalStore();
const { VEGA_NETWORKS } = useEnvironment();
const [, toggleTheme] = useThemeSwitcher();
const [theme, toggleTheme] = useThemeSwitcher();
return (
<div className="h-full dark:bg-black dark:text-white-60 bg-white relative z-0 text-black-60 grid grid-rows-[min-content,1fr]">
<AppLoader>
<div className="flex items-stretch border-b-[7px] border-vega-yellow">
<Navbar />
<div className="flex items-center gap-4 ml-auto mr-8">
<VegaWalletConnectButton
setConnectDialog={(open) => {
store.setVegaWalletConnectDialog(open);
}}
setManageDialog={(open) => {
store.setVegaWalletManageDialog(open);
}}
/>
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" />
<ThemeContext.Provider value={theme}>
<div className="h-full dark:bg-black dark:text-white-60 bg-white relative z-0 text-black-60 grid grid-rows-[min-content,1fr]">
<AppLoader>
<div className="flex items-stretch border-b-[7px] border-vega-pink dark:border-vega-yellow">
<Navbar />
<div className="flex items-center gap-4 ml-auto mr-8">
<VegaWalletConnectButton
setConnectDialog={(open) => {
store.setVegaWalletConnectDialog(open);
}}
setManageDialog={(open) => {
store.setVegaWalletManageDialog(open);
}}
/>
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" />
</div>
</div>
</div>
<main data-testid={pageProps.page}>
{/* @ts-ignore conflict between @types/react and nextjs internal types */}
<Component {...pageProps} />
</main>
<VegaConnectDialog
connectors={Connectors}
dialogOpen={store.vegaWalletConnectDialog}
setDialogOpen={(open) => store.setVegaWalletConnectDialog(open)}
/>
<VegaManageDialog
dialogOpen={store.vegaWalletManageDialog}
setDialogOpen={(open) => store.setVegaWalletManageDialog(open)}
/>
<NetworkSwitcherDialog
dialogOpen={store.vegaNetworkSwitcherDialog}
setDialogOpen={(open) => store.setVegaNetworkSwitcherDialog(open)}
onConnect={({ network }) => {
if (VEGA_NETWORKS[network]) {
push(VEGA_NETWORKS[network] ?? '');
}
}}
/>
</AppLoader>
</div>
<main data-testid={pageProps.page}>
{/* @ts-ignore conflict between @types/react and nextjs internal types */}
<Component {...pageProps} />
</main>
<VegaConnectDialog
connectors={Connectors}
dialogOpen={store.vegaWalletConnectDialog}
setDialogOpen={(open) => store.setVegaWalletConnectDialog(open)}
/>
<VegaManageDialog
dialogOpen={store.vegaWalletManageDialog}
setDialogOpen={(open) => store.setVegaWalletManageDialog(open)}
/>
<NetworkSwitcherDialog
dialogOpen={store.vegaNetworkSwitcherDialog}
setDialogOpen={(open) => store.setVegaNetworkSwitcherDialog(open)}
onConnect={({ network }) => {
if (VEGA_NETWORKS[network]) {
push(VEGA_NETWORKS[network] ?? '');
}
}}
/>
</AppLoader>
</div>
</ThemeContext.Provider>
);
}
function VegaTradingApp(props: AppProps) {
const [theme] = useThemeSwitcher();
return (
<EnvironmentProvider>
<ThemeContext.Provider value={theme}>
<VegaWalletProvider>
<Head>
<link
rel="preload"
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
<title>{t('Welcome to Vega trading!')}</title>
<link
rel="icon"
type="image/x-icon"
href="https://static.vega.xyz/favicon.ico"
/>
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
{['1', 'true'].includes(
process.env['NX_USE_ENV_OVERRIDES'] || ''
) ? (
/* eslint-disable-next-line @next/next/no-sync-scripts */
<script src="./env-config.js" type="text/javascript" />
) : null}
</Head>
<AppBody {...props} />
</VegaWalletProvider>
</ThemeContext.Provider>
<VegaWalletProvider>
<Head>
<link
rel="preload"
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
<title>{t('Welcome to Vega trading!')}</title>
<link
rel="icon"
type="image/x-icon"
href="https://static.vega.xyz/favicon.ico"
/>
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
{['1', 'true'].includes(process.env['NX_USE_ENV_OVERRIDES'] || '') ? (
/* eslint-disable-next-line @next/next/no-sync-scripts */
<script src="./env-config.js" type="text/javascript" />
) : null}
</Head>
<AppBody {...props} />
</VegaWalletProvider>
</EnvironmentProvider>
);
}

View File

@ -18,8 +18,8 @@ import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
import { SelectMarketDialog } from '@vegaprotocol/market-list';
import {
ArrowDown,
GridTab,
GridTabs,
Tab,
Tabs,
PriceCellChange,
} from '@vegaprotocol/ui-toolkit';
import type { CandleClose } from '@vegaprotocol/types';
@ -66,7 +66,7 @@ export const TradeMarketHeader = ({
<div className="flex flex-col md:flex-row gap-20 md:gap-64 ml-auto mr-8">
<button
onClick={() => setOpen(!open)}
className="shrink-0 dark:text-vega-yellow text-black text-h5 flex items-center gap-8 px-4 py-0 h-37 hover:bg-vega-yellow dark:hover:bg-white/20"
className="shrink-0 dark:text-vega-yellow text-black text-h5 flex items-center gap-8 px-4 py-0 h-37 hover:bg-black/20 dark:hover:bg-white/20"
>
<span className="break-words text-left">{market.name}</span>
<ArrowDown color="yellow" borderX={8} borderTop={12} />
@ -122,47 +122,47 @@ export const TradeGrid = ({ market }: TradeGridProps) => {
className="row-start-1 row-end-2 col-start-1 col-end-4"
/>
<TradeGridChild className="row-start-2 row-end-3 col-start-1 col-end-2">
<GridTabs>
<GridTab id="candles" name={t('Candles')}>
<Tabs>
<Tab id="candles" name={t('Candles')}>
<TradingViews.Candles marketId={market.id} />
</GridTab>
<GridTab id="depth" name={t('Depth')}>
</Tab>
<Tab id="depth" name={t('Depth')}>
<TradingViews.Depth marketId={market.id} />
</GridTab>
</GridTabs>
</Tab>
</Tabs>
</TradeGridChild>
<TradeGridChild className="row-start-2 row-end-3 col-start-2 col-end-3">
<GridTabs>
<GridTab id="ticket" name={t('Ticket')}>
<Tabs>
<Tab id="ticket" name={t('Ticket')}>
<TradingViews.Ticket marketId={market.id} />
</GridTab>
<GridTab id="info" name={t('Info')}>
</Tab>
<Tab id="info" name={t('Info')}>
<TradingViews.Info marketId={market.id} />
</GridTab>
</GridTabs>
</Tab>
</Tabs>
</TradeGridChild>
<TradeGridChild className="row-start-2 row-end-3 col-start-3 col-end-4">
<GridTabs>
<GridTab id="trades" name={t('Trades')}>
<Tabs>
<Tab id="trades" name={t('Trades')}>
<TradingViews.Trades marketId={market.id} />
</GridTab>
<GridTab id="orderbook" name={t('Orderbook')}>
</Tab>
<Tab id="orderbook" name={t('Orderbook')}>
<TradingViews.Orderbook marketId={market.id} />
</GridTab>
</GridTabs>
</Tab>
</Tabs>
</TradeGridChild>
<TradeGridChild className="col-span-3">
<GridTabs>
<GridTab id="orders" name={t('Orders')}>
<Tabs>
<Tab id="orders" name={t('Orders')}>
<TradingViews.Orders />
</GridTab>
<GridTab id="positions" name={t('Positions')}>
</Tab>
<Tab id="positions" name={t('Positions')}>
<TradingViews.Positions />
</GridTab>
<GridTab id="accounts" name={t('Accounts')}>
</Tab>
<Tab id="accounts" name={t('Accounts')}>
<TradingViews.Accounts />
</GridTab>
</GridTabs>
</Tab>
</Tabs>
</TradeGridChild>
</div>
</>

View File

@ -3,8 +3,7 @@ import { t } from '@vegaprotocol/react-helpers';
import { PositionsContainer } from '@vegaprotocol/positions';
import { OrderListContainer } from '@vegaprotocol/order-list';
import { AccountsContainer } from '@vegaprotocol/accounts';
import { AnchorButton, GridTab, GridTabs } from '@vegaprotocol/ui-toolkit';
import { AnchorButton, Tab, Tabs } from '@vegaprotocol/ui-toolkit';
import { WithdrawalsContainer } from './withdrawals/withdrawals-container';
const Portfolio = () => {
@ -20,54 +19,54 @@ const Portfolio = () => {
</h2>
</aside>
<section data-testid="portfolio-grid">
<GridTabs>
<GridTab id="positions" name={t('Positions')}>
<Tabs>
<Tab id="positions" name={t('Positions')}>
<div className={tabClassName}>
<h4 className="text-h4 text-black dark:text-white">
{t('Positions')}
</h4>
<PositionsContainer />
</div>
</GridTab>
<GridTab id="orders" name={t('Orders')}>
</Tab>
<Tab id="orders" name={t('Orders')}>
<div className={tabClassName}>
<h4 className="text-h4 text-black dark:text-white">
{t('Orders')}
</h4>
<OrderListContainer />
</div>
</GridTab>
<GridTab id="fills" name={t('Fills')}>
</Tab>
<Tab id="fills" name={t('Fills')}>
<div className={tabClassName}>
<h4 className="text-h4 text-black dark:text-white">
{t('Fills')}
</h4>
</div>
</GridTab>
<GridTab id="history" name={t('History')}>
</Tab>
<Tab id="history" name={t('History')}>
<div className={tabClassName}>
<h4 className="text-h4 text-black dark:text-white">
{t('History')}
</h4>
</div>
</GridTab>
</GridTabs>
</Tab>
</Tabs>
</section>
</main>
<section className="fixed bottom-0 left-0 w-full h-[200px]">
<GridTabs>
<GridTab id="collateral" name={t('Collateral')}>
<Tabs>
<Tab id="collateral" name={t('Collateral')}>
<AccountsContainer />
</GridTab>
<GridTab id="deposits" name={t('Deposits')}>
</Tab>
<Tab id="deposits" name={t('Deposits')}>
<AnchorButton data-testid="deposit" href="/portfolio/deposit">
{t('Deposit')}
</AnchorButton>
</GridTab>
<GridTab id="withdrawals" name={t('Withdrawals')}>
</Tab>
<Tab id="withdrawals" name={t('Withdrawals')}>
<WithdrawalsContainer />
</GridTab>
</GridTabs>
</Tab>
</Tabs>
</section>
</div>
</Web3Container>

View File

@ -17,7 +17,6 @@ import { useContext, useMemo, useState } from 'react';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { ThemeContext } from '@vegaprotocol/react-helpers';
import {
Button,
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
@ -58,14 +57,14 @@ export const CandlesChartContainer = ({
return new VegaDataSource(client, marketId, keypair?.pub);
}, [client, marketId, keypair]);
const dropdownTriggerStyles = 'border-black-60 dark:border-white-60 px-20';
return (
<div className="h-full flex flex-col">
<div className="p-8 flex flex-row flex-wrap gap-8">
<DropdownMenu>
<DropdownMenuTrigger asChild={true}>
<Button appendIconName="caret-down" variant="secondary">
{t('Interval')}
</Button>
<DropdownMenuTrigger className={dropdownTriggerStyles}>
{t('Interval')}
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuRadioGroup
@ -90,10 +89,8 @@ export const CandlesChartContainer = ({
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger asChild={true}>
<Button appendIconName="caret-down" variant="secondary">
<Icon name={chartTypeIcon.get(chartType) as IconName} />
</Button>
<DropdownMenuTrigger className={dropdownTriggerStyles}>
<Icon name={chartTypeIcon.get(chartType) as IconName} />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuRadioGroup
@ -114,10 +111,8 @@ export const CandlesChartContainer = ({
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger asChild={true}>
<Button appendIconName="caret-down" variant="secondary">
{t('Overlays')}
</Button>
<DropdownMenuTrigger className={dropdownTriggerStyles}>
{t('Overlays')}
</DropdownMenuTrigger>
<DropdownMenuContent>
{Object.values(Overlay).map((overlay) => (
@ -145,10 +140,8 @@ export const CandlesChartContainer = ({
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger asChild={true}>
<Button appendIconName="caret-down" variant="secondary">
{t('Studies')}
</Button>
<DropdownMenuTrigger className={dropdownTriggerStyles}>
{t('Studies')}
</DropdownMenuTrigger>
<DropdownMenuContent>
{Object.values(Study).map((study) => (

View File

@ -109,7 +109,7 @@ export const DealTicket = ({
)}
<Button
className="w-full mb-8"
variant="primary"
variant="trade"
type="submit"
disabled={isDisabled}
data-testid="place-order"

View File

@ -37,7 +37,6 @@ beforeEach(() => {
submitDeposit: jest.fn(),
requestFaucet: jest.fn(),
limits: {
min: new BigNumber(0),
max: new BigNumber(20),
},
allowance: new BigNumber(30),
@ -199,6 +198,6 @@ it('Deposit', async () => {
// @ts-ignore contract address definitely defined
assetSource: asset.source.contractAddress,
amount: '1500',
vegaPublicKey: `0x${vegaKey}`,
vegaPublicKey: vegaKey,
});
});

View File

@ -89,7 +89,7 @@ export const DepositForm = ({
submitDeposit({
assetSource: selectedAsset.source.contractAddress,
amount: removeDecimal(fields.amount, selectedAsset.decimals),
vegaPublicKey: `0x${fields.to}`,
vegaPublicKey: fields.to,
});
};

View File

@ -1,6 +1,7 @@
import type { BigNumber } from 'ethers';
import { ethers } from 'ethers';
import abi from '../abis/erc20_bridge_new_abi.json';
import { prepend0x } from '../utils';
export class CollateralBridgeNew {
public contract: ethers.Contract;
@ -14,7 +15,11 @@ export class CollateralBridgeNew {
}
depositAsset(assetSource: string, amount: string, vegaPublicKey: string) {
return this.contract.deposit_asset(assetSource, amount, vegaPublicKey);
return this.contract.deposit_asset(
assetSource,
amount,
prepend0x(vegaPublicKey)
);
}
getAssetSource(vegaAssetId: string) {
return this.contract.get_asset_source(vegaAssetId);

View File

@ -1,6 +1,7 @@
import type { BigNumber } from 'ethers';
import { ethers } from 'ethers';
import abi from '../abis/erc20_bridge_abi.json';
import { prepend0x } from '../utils';
export class CollateralBridge {
public contract: ethers.Contract;
@ -16,7 +17,11 @@ export class CollateralBridge {
}
depositAsset(assetSource: string, amount: string, vegaPublicKey: string) {
return this.contract.deposit_asset(assetSource, amount, vegaPublicKey);
return this.contract.deposit_asset(
assetSource,
amount,
prepend0x(vegaPublicKey)
);
}
getAssetSource(vegaAssetId: string) {
return this.contract.get_asset_source(vegaAssetId);

View File

@ -1,5 +1,6 @@
import { ethers } from 'ethers';
import abi from '../abis/staking_abi.json';
import { prepend0x } from '../utils';
export class StakingBridge {
public contract: ethers.Contract;
@ -14,19 +15,23 @@ export class StakingBridge {
}
stake(amount: string, vegaPublicKey: string) {
return this.contract.stake(amount, `0x${vegaPublicKey}`);
return this.contract.stake(amount, prepend0x(vegaPublicKey));
}
removeStake(amount: string, vegaPublicKey: string) {
return this.contract.remove_stake(amount, `0x${vegaPublicKey}`);
return this.contract.remove_stake(amount, prepend0x(vegaPublicKey));
}
transferStake(amount: string, newAddress: string, vegaPublicKey: string) {
return this.contract.transfer_stake(amount, newAddress, vegaPublicKey);
return this.contract.transfer_stake(
amount,
newAddress,
prepend0x(vegaPublicKey)
);
}
stakingToken() {
return this.contract.staking_token();
}
stakeBalance(target: string, vegaPublicKey: string) {
return this.contract.stake_balance(target, vegaPublicKey);
return this.contract.stake_balance(target, prepend0x(vegaPublicKey));
}
totalStaked() {
return this.contract.total_staked();

View File

@ -1,5 +1,6 @@
import { ethers } from 'ethers';
import abi from '../abis/vesting_abi.json';
import { prepend0x } from '../utils';
export class TokenVesting {
public contract: ethers.Contract;
@ -14,13 +15,13 @@ export class TokenVesting {
}
stakeTokens(amount: string, vegaPublicKey: string) {
return this.contract.stake_tokens(amount, vegaPublicKey);
return this.contract.stake_tokens(amount, prepend0x(vegaPublicKey));
}
removeStake(amount: string, vegaPublicKey: string) {
return this.contract.remove_stake(amount, vegaPublicKey);
return this.contract.remove_stake(amount, prepend0x(vegaPublicKey));
}
stakeBalance(address: string, vegaPublicKey: string) {
return this.contract.stake_balance(address, vegaPublicKey);
return this.contract.stake_balance(address, prepend0x(vegaPublicKey));
}
totalStaked() {
return this.contract.total_staked();

View File

@ -1,6 +0,0 @@
import { hexadecimalify } from './hexadecimalify';
test('Prepends strings with 0x', () => {
expect(hexadecimalify('abc')).toEqual('0xabc');
expect(hexadecimalify('123456789')).toEqual('0x123456789');
});

View File

@ -1,3 +0,0 @@
export function hexadecimalify(str: string) {
return `0x${str}`;
}

View File

@ -1,2 +1,2 @@
export * from './ascii-to-hex';
export * from './hexadecimalify';
export * from './prepend-0x';

View File

@ -0,0 +1,6 @@
import { prepend0x } from './prepend-0x';
test('Prepends strings with 0x', () => {
expect(prepend0x('abc')).toEqual('0xabc');
expect(prepend0x('123456789')).toEqual('0x123456789');
});

View File

@ -0,0 +1,3 @@
export function prepend0x(str: string) {
return `0x${str}`;
}

View File

@ -6,6 +6,73 @@ const shadeOfGray = (shade) => {
return `#${hexValue}${hexValue}${hexValue}`;
};
const colours = {
transparent: 'transparent',
current: 'currentColor',
text: '#C7C7C7',
deemphasise: '#8A9BA8',
white: {
DEFAULT: '#FFF',
strong: '#FFF',
normal: '#F5F8FA',
muted: '#676767',
'02': shadeOfGray(2),
'05': shadeOfGray(5),
10: shadeOfGray(10),
25: shadeOfGray(25),
40: shadeOfGray(40),
60: shadeOfGray(60),
70: shadeOfGray(70),
80: shadeOfGray(80),
90: shadeOfGray(90),
95: shadeOfGray(95),
100: shadeOfGray(100),
},
black: {
DEFAULT: '#000',
strong: '#000',
normal: '#000',
muted: '#BFCCD6',
'02': shadeOfGray(100 - 2),
'05': shadeOfGray(100 - 5),
10: shadeOfGray(100 - 10),
25: shadeOfGray(100 - 25),
40: shadeOfGray(100 - 40),
50: shadeOfGray(100 - 50),
60: shadeOfGray(100 - 60),
70: shadeOfGray(100 - 70),
80: shadeOfGray(100 - 80),
90: shadeOfGray(100 - 90),
95: shadeOfGray(100 - 95),
100: shadeOfGray(100 - 100),
},
vega: {
yellow: '#DFFF0B',
'yellow-dark': '#474B0A',
pink: '#FF077F',
green: '#00F780',
'green-medium': '#00DE73',
'green-dark': '#008545',
red: '#FF261A',
'red-dark': '#EB001B',
},
blue: '#1DA2FB',
coral: '#FF6057',
pink: '#FF2D5E',
orange: '#D9822B',
danger: '#FF261A',
warning: '#FF7A1A',
selected: '#DFFF0B',
success: '#00F780',
'danger-bg': '#9E0025', // for white text
};
const boxShadowPosition = {
outer: '2px 2px 0 0',
insetUnderline: 'inset 0 -2px 0 0',
insetShading: 'inset 2px 2px 6px',
};
module.exports = {
screens: {
xs: '500px',
@ -16,67 +83,7 @@ module.exports = {
xxl: '1536px',
},
colors: {
transparent: 'transparent',
current: 'currentColor',
vega: {
yellow: '#EDFF22',
pink: '#FF2D5E',
green: '#00F780',
red: '#FF261A',
},
red: {
DEFAULT: '#ED1515',
dark: '#EB001B',
bar: 'rgba(47, 246, 139, 0.45)', // #2FF68B 45%
},
green: {
DEFAULT: '#26FF8A',
dark: '#008545',
bar: 'rgba(47, 246, 139, 0.45)', // #2FF68B 45%
},
text: '#C7C7C7',
deemphasise: '#8A9BA8',
white: {
DEFAULT: '#FFF',
strong: '#FFF',
normal: '#F5F8FA',
muted: '#676767',
'02': shadeOfGray(2),
'05': shadeOfGray(5),
10: shadeOfGray(10),
25: shadeOfGray(25),
40: shadeOfGray(40),
60: shadeOfGray(60),
80: shadeOfGray(80),
95: shadeOfGray(95),
100: shadeOfGray(100),
},
black: {
DEFAULT: '#000',
strong: '#000',
normal: '#000',
muted: '#BFCCD6',
'02': shadeOfGray(100 - 2),
'05': shadeOfGray(100 - 5),
10: shadeOfGray(100 - 10),
25: shadeOfGray(100 - 25),
40: shadeOfGray(100 - 40),
60: shadeOfGray(100 - 60),
80: shadeOfGray(100 - 80),
95: shadeOfGray(100 - 95),
100: shadeOfGray(100 - 100),
},
blue: '#1DA2FB',
coral: '#FF6057',
orange: '#D9822B',
yellow: {
DEFAULT: '#EDFF22',
dark: '#474B0A', // yellow 0.3 opacity on black
},
danger: '#FF261A',
warning: '#FF7A1A',
success: '#26FF8A',
'danger-bg': '#9E0025', // for white text
...colours,
},
spacing: {
0: '0px',
@ -121,7 +128,9 @@ module.exports = {
borderWidth: {
DEFAULT: '1px',
1: '1px',
2: '2px',
4: '4px',
7: '7px',
},
borderRadius: {
none: '0',
@ -178,11 +187,27 @@ module.exports = {
ui: ['14px', '20px'],
'ui-small': ['12px', '16px'],
},
boxShadow: {
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',
radio: '1px 1px 0 0',
intent: `3px 3px 0 0`,
'vega-yellow': `${boxShadowPosition.outer} ${colours.vega.yellow}`,
'vega-pink': `${boxShadowPosition.outer} ${colours.vega.pink}`,
'inset-black': `${boxShadowPosition.insetUnderline} ${colours.black.DEFAULT}`,
'inset-white': `${boxShadowPosition.insetUnderline} ${colours.white.DEFAULT}`,
'inset-vega-yellow': `${boxShadowPosition.insetUnderline} ${colours.vega.yellow}`,
'inset-vega-pink': `${boxShadowPosition.insetUnderline} ${colours.vega.pink}`,
'inset-danger': `${boxShadowPosition.insetUnderline} ${colours.danger}`,
input: `${boxShadowPosition.insetShading} ${colours.white['80']}`,
'input-dark': `${boxShadowPosition.insetShading} ${colours.black['80']}`,
'input-focus': `${boxShadowPosition.insetShading} ${colours.white['80']}, ${boxShadowPosition.insetUnderline} ${colours.vega.pink}`,
'input-focus-dark': `${boxShadowPosition.insetShading} ${colours.black['80']}, ${boxShadowPosition.insetUnderline} ${colours.vega.yellow}`,
'input-focus-error': `${boxShadowPosition.insetShading} ${colours.white['80']}, ${boxShadowPosition.insetUnderline} ${colours.danger}`,
'input-focus-error-dark': `${boxShadowPosition.insetShading} ${colours.black['80']}, ${boxShadowPosition.insetUnderline} ${colours.danger}`,
'checkbox-focus': `${boxShadowPosition.insetShading} ${colours.white['80']}, ${boxShadowPosition.outer} ${colours.vega.pink}`,
'checkbox-focus-dark': `${boxShadowPosition.insetShading} ${colours.black['80']}, ${boxShadowPosition.outer} ${colours.vega.yellow}`,
},
backgroundImage: {
'fairground-nav': "url('https://static.vega.xyz/fairground-nav-bg.jpg')",
},
};

View File

@ -29,6 +29,25 @@ const vegaCustomClasses = plugin(function ({ addUtilities }) {
'.syntax-highlighter-wrapper .hljs-string': {
color: theme.colors.blue,
},
'.input-border': {
borderWidth: '1px',
borderStyle: 'solid',
borderTopColor: theme.colors.black['60'],
borderLeftColor: theme.colors.black['60'],
borderRightColor: theme.colors.black['40'],
borderBottomColor: theme.colors.black['40'],
},
'.input-border-dark': {
borderWidth: '1px',
borderStyle: 'solid',
borderTopColor: theme.colors.white['40'],
borderLeftColor: theme.colors.white['40'],
borderRightColor: theme.colors.white['60'],
borderBottomColor: theme.colors.white['60'],
},
'.color-scheme-dark': {
colorScheme: 'dark',
},
});
});

View File

@ -14,7 +14,7 @@ import BigNumber from 'bignumber.js';
import { sortTrades } from './trades-data-provider';
export const UP_CLASS = 'text-vega-green';
export const DOWN_CLASS = 'text-vega-pink';
export const DOWN_CLASS = 'text-vega-red';
const changeCellClass =
(dataKey: string) =>

View File

@ -9,7 +9,7 @@ const agGridLightVariables = `
--ag-header-background-color: ${theme.colors.white[100]};
--ag-odd-row-background-color: ${theme.colors.white[100]};
--ag-row-border-color: ${theme.colors.white[100]};
--ag-row-hover-color: ${theme.colors.yellow.DEFAULT};
--ag-row-hover-color: ${theme.colors.black[10]};
--ag-font-size: 12px;
}

View File

@ -11,7 +11,7 @@ const Template: Story = (args) => (
<div className="mb-8">
<Button {...args} />
</div>
{args['variant'] !== 'inline' && <Button {...args} disabled />}
{args['variant'] !== 'inline-link' && <Button {...args} disabled />}
</>
);
@ -26,18 +26,18 @@ Secondary.args = {
variant: 'secondary',
};
export const Trade = Template.bind({});
Trade.args = {
children: 'Trade',
variant: 'trade',
};
export const Accent = Template.bind({});
Accent.args = {
children: 'Accent',
variant: 'accent',
};
export const Inline = Template.bind({});
Inline.args = {
children: 'Inline',
variant: 'inline',
};
export const InlineLink = Template.bind({});
InlineLink.args = {
children: 'Inline link',
@ -67,13 +67,13 @@ export const NavAccent: Story = () => (
export const NavInline: Story = () => (
<>
<div className="mb-8">
<Button variant="inline" className="uppercase">
<Button variant="inline-link" className="uppercase">
Background
</Button>
</div>
<div className="mb-8">
<Button
variant="inline"
variant="inline-link"
className="uppercase"
prependIconName="menu-open"
>
@ -82,7 +82,7 @@ export const NavInline: Story = () => (
</div>
<div className="mb-8">
<Button
variant="inline"
variant="inline-link"
className="uppercase"
appendIconName="menu-closed"
>
@ -96,12 +96,33 @@ export const IconPrepend = Template.bind({});
IconPrepend.args = {
children: 'Icon prepend',
prependIconName: 'search',
variant: 'accent',
variant: 'trade',
};
export const IconAppend = Template.bind({});
IconAppend.args = {
children: 'Icon append',
appendIconName: 'search',
variant: 'accent',
variant: 'trade',
};
export const InlineIconPrepend = Template.bind({});
InlineIconPrepend.args = {
children: 'Icon prepend',
prependIconName: 'search',
variant: 'inline-link',
};
export const InlineIconAppend = Template.bind({});
InlineIconAppend.args = {
children: 'Icon append',
appendIconName: 'search',
variant: 'inline-link',
};
export const SpanWithButtonStyleAndContent = Template.bind({});
SpanWithButtonStyleAndContent.args = {
children: 'Apply button styles to other elements (i.e. span, <Link>)',
appendIconName: 'search',
variant: 'trade',
};

View File

@ -1,4 +1,8 @@
import type { AnchorHTMLAttributes, ButtonHTMLAttributes } from 'react';
import type {
AnchorHTMLAttributes,
ButtonHTMLAttributes,
ReactNode,
} from 'react';
import { forwardRef } from 'react';
import classNames from 'classnames';
import type { IconName } from '../icon';
@ -9,13 +13,15 @@ import {
includesBorderWidth,
includesHeight,
} from '../../utils/class-names';
import classnames from 'classnames';
interface CommonProps {
children?: React.ReactNode;
variant?: 'primary' | 'secondary' | 'accent' | 'inline' | 'inline-link';
children?: ReactNode;
variant?: 'primary' | 'secondary' | 'trade' | 'accent' | 'inline-link';
className?: string;
prependIconName?: IconName;
appendIconName?: IconName;
boxShadow?: boolean;
}
export interface ButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement>,
@ -25,21 +31,30 @@ export interface AnchorButtonProps
extends AnchorHTMLAttributes<HTMLAnchorElement>,
CommonProps {}
const getClasses = (
variant: CommonProps['variant'],
paddingLeftProvided: boolean,
paddingRightProvided: boolean,
borderWidthProvided: boolean,
heightProvided: boolean
export const getButtonClasses = (
className?: string,
variant?: 'primary' | 'secondary' | 'trade' | 'accent' | 'inline-link',
boxShadow?: boolean
) => {
const paddingLeftProvided = includesLeftPadding(className);
const paddingRightProvided = includesRightPadding(className);
const borderWidthProvided = includesBorderWidth(className);
const heightProvided = includesHeight(className);
// Add classes into variables if there are multiple classes shared in multiple button styles
const sharedClasses =
'inline-flex items-center justify-center box-border transition-all disabled:no-underline';
const underlineOnHover = 'no-underline hover:underline';
const commonHoverAndActiveBorder =
'hover:border-black dark:hover:border-white active:border-black dark:active:border-white';
'inline-flex items-center justify-center box-border transition-[background-color] ease-linear duration-50 disabled:no-underline';
const commonButtonClasses = classnames(
'relative disabled:static',
'text-ui font-semibold focus-visible:outline-none border no-underline hover:no-underline',
{
'shadow-none': !boxShadow,
'shadow-[3px_3px_0_0] focus-visible:shadow-vega-pink dark:focus-visible:shadow-vega-yellow active:top-[1px] active:left-[1px] active:shadow-[2px_2px_0_0]':
boxShadow === undefined || boxShadow,
}
);
const commonDisabled =
'disabled:bg-black-10 dark:disabled:bg-white-10 disabled:text-black-60 dark:disabled:text-white-60 disabled:border-black-25 dark:disabled:border-white-25';
'disabled:bg-black-10 dark:disabled:bg-white-10 disabled:text-black-60 dark:disabled:text-white-60 disabled:border-black-25 dark:disabled:border-white-25 disabled:shadow-none dark:disabled:shadow-none';
const inlineTextColour =
'text-black-95 dark:text-white-95 hover:text-black hover:dark:text-white active:text-black dark:active:text-vega-yellow';
@ -62,104 +77,92 @@ const getClasses = (
const primaryClasses = [
sharedClasses,
commonHoverAndActiveBorder,
underlineOnHover,
commonButtonClasses,
commonDisabled,
standardButtonPaddingLeft,
standardButtonPaddingRight,
standardButtonBorderWidth,
buttonHeight,
'bg-black dark:bg-white hover:bg-black-80 dark:hover:bg-white-80 active:bg-white dark:active:bg-black',
'text-ui text-white dark:text-black active:text-black dark:active:text-white',
'bg-black dark:bg-white hover:bg-black-80 dark:hover:bg-white-90 active:bg-black-80 dark:active:bg-white-90',
'border-white dark:border-black shadow-black active:shadow-black dark:shadow-white-80 dark:active:shadow-white',
'text-white dark:text-black',
];
const secondaryClasses = [
sharedClasses,
commonHoverAndActiveBorder,
underlineOnHover,
commonButtonClasses,
commonDisabled,
standardButtonPaddingLeft,
standardButtonPaddingRight,
standardButtonBorderWidth,
buttonHeight,
'bg-white dark:bg-black hover:bg-black-25 dark:hover:bg-white-25 active:bg-black dark:active:bg-white',
'text-ui text-black dark:text-white active:text-white dark:active:text-black',
'border-black-60 dark:border-white-60 hover:border-black',
'bg-white dark:bg-black hover:bg-black-25 dark:hover:bg-white-25',
'border-black dark:border-white shadow-black dark:shadow-white',
'text-black dark:text-white',
];
const tradeClasses = [
sharedClasses,
commonButtonClasses,
commonDisabled,
standardButtonPaddingLeft,
standardButtonPaddingRight,
standardButtonBorderWidth,
buttonHeight,
'bg-vega-green hover:bg-vega-green-medium',
'border-black disabled:shadow-none dark:disabled:shadow-none shadow-black dark:shadow-white',
'text-black',
];
const accentClasses = [
sharedClasses,
commonHoverAndActiveBorder,
underlineOnHover,
commonDisabled,
standardButtonPaddingLeft,
standardButtonPaddingRight,
standardButtonBorderWidth,
buttonHeight,
'bg-vega-yellow dark:bg-vega-yellow hover:bg-yellow/dark dark:hover:bg-vega-yellow/30 active:bg-white dark:active:bg-black',
'text-ui uppercase text-black dark:text-black hover:text-white dark:hover:text-white active:text-black dark:active:text-white',
'bg-vega-yellow dark:bg-vega-yellow hover:bg-vega-yellow-dark dark:hover:bg-vega-yellow-dark active:bg-white dark:active:bg-black',
'uppercase text-black dark:text-black hover:text-white dark:hover:text-white active:text-black dark:active:text-white',
'border-transparent dark:border-transparent',
];
const inlineClasses = [
sharedClasses,
inlineButtonPaddingLeft,
inlineButtonPaddingRight,
buttonHeight,
inlineTextColour,
'border-none',
'text-ui',
];
const inlineLinkClasses = [
sharedClasses,
inlineButtonPaddingLeft,
inlineButtonPaddingRight,
buttonHeight,
inlineTextColour,
'underline hover:underline',
'underline hover:underline hover:text-black-60 dark:hover:text-white-80',
'border-none',
];
let variantClasses: string[];
switch (variant) {
case 'primary':
return primaryClasses;
variantClasses = primaryClasses;
break;
case 'secondary':
return secondaryClasses;
variantClasses = secondaryClasses;
break;
case 'trade':
variantClasses = tradeClasses;
break;
case 'accent':
return accentClasses;
case 'inline':
return inlineClasses;
variantClasses = accentClasses;
break;
case 'inline-link':
return inlineLinkClasses;
variantClasses = inlineLinkClasses;
break;
default:
return '';
variantClasses = [''];
}
return classNames(...variantClasses, className);
};
const classes = (
className: CommonProps['className'],
variant: CommonProps['variant']
) => {
const paddingLeftProvided = includesLeftPadding(className);
const paddingRightProvided = includesRightPadding(className);
const borderWidthProvided = includesBorderWidth(className);
const heightProvided = includesHeight(className);
return classNames(
getClasses(
variant,
paddingLeftProvided,
paddingRightProvided,
borderWidthProvided,
heightProvided
),
className
);
};
const getContent = (
children: React.ReactNode,
export const getButtonContent = (
children: ReactNode,
prependIconName?: IconName,
appendIconName?: IconName
) => {
@ -190,6 +193,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
className,
prependIconName,
appendIconName,
boxShadow,
...props
},
ref
@ -197,11 +201,11 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
return (
<button
ref={ref}
className={classes(className, variant)}
className={getButtonClasses(className, variant, boxShadow)}
type={type}
{...props}
>
{getContent(children, prependIconName, appendIconName)}
{getButtonContent(children, prependIconName, appendIconName)}
</button>
);
}
@ -215,13 +219,18 @@ export const AnchorButton = forwardRef<HTMLAnchorElement, AnchorButtonProps>(
className,
prependIconName,
appendIconName,
boxShadow,
...props
},
ref
) => {
return (
<a ref={ref} className={classes(className, variant)} {...props}>
{getContent(children, prependIconName, appendIconName)}
<a
ref={ref}
className={getButtonClasses(className, variant, boxShadow)}
{...props}
>
{getButtonContent(children, prependIconName, appendIconName)}
</a>
);
}

View File

@ -17,31 +17,29 @@ it('renders title and icon', () => {
it(`Applies class for success intent`, () => {
render(<Callout intent={Intent.Danger} />);
expect(screen.getByTestId('callout')).toHaveClass('shadow-danger');
expect(screen.getByTestId('callout')).toHaveClass('border-danger');
});
it(`Applies class for warning intent`, () => {
render(<Callout intent={Intent.Warning} />);
expect(screen.getByTestId('callout')).toHaveClass('shadow-warning');
expect(screen.getByTestId('callout')).toHaveClass('border-warning');
});
it(`Applies class for danger intent`, () => {
render(<Callout intent={Intent.Danger} />);
expect(screen.getByTestId('callout')).toHaveClass('shadow-danger');
expect(screen.getByTestId('callout')).toHaveClass('border-danger');
});
it(`Applies class for primary intent`, () => {
render(<Callout intent={Intent.Primary} />);
expect(screen.getByTestId('callout')).toHaveClass(
'shadow-vega-pink',
'dark:shadow-vega-yellow'
'border-black dark:border-white'
);
});
it(`Applies class for none intent`, () => {
render(<Callout />);
expect(screen.getByTestId('callout')).toHaveClass(
'shadow-black',
'dark:shadow-white'
'border-black dark:border-white'
);
});

View File

@ -21,31 +21,31 @@ const Template: ComponentStory<typeof Callout> = (args) => (
export const Default = Template.bind({});
Default.args = {
children: 'Content',
children: 'No intent supplied',
};
export const Primary = Template.bind({});
Primary.args = {
intent: Intent.Primary,
children: 'Content',
children: 'Intent: Primary',
};
export const Danger = Template.bind({});
Danger.args = {
intent: Intent.Danger,
children: 'Content',
children: 'Intent: Danger',
};
export const Warning = Template.bind({});
Warning.args = {
intent: Intent.Warning,
children: 'Content',
children: 'Intent: Warning',
};
export const Success = Template.bind({});
Success.args = {
intent: Intent.Success,
children: 'Content',
children: 'Intent: Success',
};
export const IconAndContent = Template.bind({});
@ -55,7 +55,7 @@ IconAndContent.args = {
iconName: 'endorsed',
children: (
<div className="flex flex-col">
<div>With a longer explaination</div>
<div>With a longer explanation</div>
<Button className="block mt-8" variant="secondary">
Action
</Button>
@ -74,7 +74,7 @@ CustomIconAndContent.args = {
),
children: (
<div className="flex flex-col">
<div>With a longer explaination</div>
<div>With a longer explanation</div>
<Button className="block mt-8" variant="secondary">
Action
</Button>
@ -89,7 +89,7 @@ Loading.args = {
isLoading: true,
children: (
<div className="flex flex-col">
<div>With a longer explaination</div>
<div>With a longer explanation</div>
<Button className="block mt-8" variant="secondary">
Action
</Button>

View File

@ -1,13 +1,13 @@
import type { ReactNode } from 'react';
import type { ReactNode, ReactElement } from 'react';
import classNames from 'classnames';
import { getIntentShadow, Intent } from '../../utils/intent';
import { getIntentBorder, Intent } from '../../utils/intent';
import { Loader } from '../loader';
import type { IconName } from '../icon';
import { Icon } from '../icon';
interface CalloutRootProps {
children?: React.ReactNode;
title?: React.ReactElement | string;
children?: ReactNode;
title?: ReactElement | string;
intent?: Intent;
headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
isLoading?: boolean;
@ -89,13 +89,10 @@ export function Callout({
const className = classNames(
'flex gap-20',
'border',
'border-black',
'dark:border-white',
'text-body-large',
'dark:text-white',
'p-16',
getIntentShadow(intent)
getIntentBorder(intent)
);
const TitleTag: keyof JSX.IntrinsicElements = headingLevel
? `h${headingLevel}`

View File

@ -0,0 +1,64 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { Checkbox } from './checkbox';
describe('Checkbox', () => {
it('should render checkbox with label successfully', () => {
render(<Checkbox label="test" />);
expect(screen.getByText('test')).toBeInTheDocument();
});
it('should render a checked checkbox if specified in state', () => {
render(<Checkbox label="checked" state={'checked'} />);
expect(screen.getByTestId('checkbox-checked')).toBeInTheDocument();
});
it('should render an unchecked checkbox if specified in state', () => {
render(<Checkbox label="unchecked" state={'unchecked'} />);
expect(screen.getByTestId('checkbox-unchecked')).toBeInTheDocument();
});
it('should render an indeterminate checkbox if specified in state', () => {
render(<Checkbox label="indeterminate" state={'indeterminate'} />);
expect(screen.getByTestId('checkbox-indeterminate')).toBeInTheDocument();
});
it('should render a checkbox in error if specified', () => {
render(<Checkbox label="error" error={true} />);
expect(screen.getByTestId('checkbox-error')).toBeInTheDocument();
});
it('should render a checked checkbox in error if specified', () => {
render(<Checkbox label="error-checked" state={'checked'} error={true} />);
expect(screen.getByTestId('checkbox-error-checked')).toBeInTheDocument();
});
it('should render an unchecked checkbox in error if specified', () => {
render(
<Checkbox label="error-unchecked" state={'unchecked'} error={true} />
);
expect(screen.getByTestId('checkbox-error-unchecked')).toBeInTheDocument();
});
it('should render an indeterminate checkbox in error if specified', () => {
render(
<Checkbox
label="error-indeterminate"
state={'indeterminate'}
error={true}
/>
);
expect(
screen.getByTestId('checkbox-error-indeterminate')
).toBeInTheDocument();
});
it('fires callback on change if provided', () => {
const callback = jest.fn();
render(<Checkbox label="onchange" onChange={callback} />);
const checkbox = screen.getByText('onchange');
fireEvent.click(checkbox);
expect(callback.mock.calls.length).toEqual(1);
});
});

View File

@ -0,0 +1,45 @@
import type { ComponentStory, ComponentMeta } from '@storybook/react';
import { Checkbox } from './checkbox';
import { useState } from 'react';
export default {
component: Checkbox,
title: 'Checkbox',
} as ComponentMeta<typeof Checkbox>;
export const Controlled: ComponentStory<typeof Checkbox> = () => {
const [checkboxState, setCheckboxState] = useState<
'checked' | 'unchecked' | 'indeterminate'
>('indeterminate');
return (
<Checkbox
label={'controlled - initially indeterminate'}
state={checkboxState}
onChange={() => {
if (
checkboxState === 'indeterminate' ||
checkboxState === 'unchecked'
) {
setCheckboxState('checked');
}
if (checkboxState === 'checked') {
setCheckboxState('unchecked');
}
}}
/>
);
};
export const Default: ComponentStory<typeof Checkbox> = () => (
<Checkbox label={'uncontrolled - default checkbox behaviour'} />
);
export const Error: ComponentStory<typeof Checkbox> = () => (
<Checkbox label={'error'} error={true} />
);
export const Disabled: ComponentStory<typeof Checkbox> = () => (
<Checkbox label={'disabled'} disabled />
);

View File

@ -0,0 +1,81 @@
import classnames from 'classnames';
import { Icon } from '../icon';
import type { ChangeEvent, InputHTMLAttributes } from 'react';
export interface CheckboxProps extends InputHTMLAttributes<HTMLInputElement> {
label: string;
className?: string;
state?: 'checked' | 'unchecked' | 'indeterminate';
error?: boolean;
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
}
export const Checkbox = ({
label,
className,
state,
error,
onChange,
...props
}: CheckboxProps) => {
const containerClasses = classnames(
className,
'grid grid-cols-[auto_1fr] select-none'
);
const inputClasses = 'sr-only peer';
const vegaCheckboxClasses = classnames(
'col-start-1 row-start-1',
'inline-block w-20 h-20 relative z-0',
'shadow-input dark:shadow-input-dark bg-white dark:bg-white-25',
'focus-visible:outline-none focus-visible:shadow-checkbox-focus dark:focus-visible:shadow-checkbox-focus-dark',
'cursor-pointer peer-disabled:cursor-default',
{
'input-border dark:input-border-dark': !error,
'border border-vega-red': error,
}
);
// In uncontrolled elements without state, we apply the hidden class and 'peer-checked' can
// override it as necessary. At other times, we control display properties via state conditions.
const iconClasses = classnames(
'col-start-1 row-start-1 place-self-center relative z-50 pointer-events-none',
{
hidden: !state || state === 'unchecked',
}
);
const tickClasses = classnames(iconClasses, {
'peer-checked:block': !state,
block: state === 'checked',
hidden: state === 'indeterminate',
});
const minusClasses = classnames(iconClasses, {
block: state === 'indeterminate',
hidden: state === 'checked',
});
const labelClasses = classnames(
'col-start-2 row-start-1 pl-8 cursor-pointer peer-disabled:cursor-default'
);
return (
<label
className={containerClasses}
data-testid={`checkbox${error ? '-error' : ''}${
state ? `-${state}` : ''
}`}
>
<input
type="checkbox"
className={inputClasses}
onChange={onChange}
{...props}
/>
<span className={vegaCheckboxClasses} />
<span className={tickClasses}>
<Icon name={'tick'} className="fill-vega-pink dark:fill-vega-yellow" />
</span>
<span className={minusClasses}>
<Icon name={'minus'} className="fill-vega-pink dark:fill-vega-yellow" />
</span>
<span className={labelClasses}>{label}</span>
</label>
);
};

View File

@ -0,0 +1 @@
export * from './checkbox';

View File

@ -15,7 +15,9 @@ const Template: ComponentStory<typeof Dialog> = (args) => {
return (
<div>
<Button onClick={() => setOpen(true)}>Open dialog</Button>
<Dialog {...args} open={open} setOpen={setOpen} />
<Dialog {...args} open={open} onChange={setOpen}>
{args.children}
</Dialog>
</div>
);
};
@ -23,38 +25,38 @@ const Template: ComponentStory<typeof Dialog> = (args) => {
export const Default = Template.bind({});
Default.args = {
open: false,
title: 'Title',
title: 'No intent supplied',
children: <p>Some content</p>,
};
export const Primary = Template.bind({});
Primary.args = {
open: false,
title: 'Intent: Primary',
children: <p>Some content</p>,
intent: Intent.Primary,
};
export const Danger = Template.bind({});
Danger.args = {
open: false,
title: 'Danger',
title: 'Intent: Danger',
children: <p>Some content</p>,
intent: Intent.Danger,
};
export const Success = Template.bind({});
Success.args = {
open: false,
title: 'Success',
children: <p>Some content</p>,
intent: Intent.Success,
};
export const Warning = Template.bind({});
Warning.args = {
open: false,
title: 'Warning',
title: 'Intent: Warning',
children: <p>Some content</p>,
intent: Intent.Warning,
};
export const Modal = Template.bind({});
Modal.args = {
export const Success = Template.bind({});
Success.args = {
open: false,
title: 'Modal (Prompt)',
title: 'Intent: Success',
children: <p>Some content</p>,
intent: Intent.Warning,
intent: Intent.Success,
};

View File

@ -2,7 +2,7 @@ import * as DialogPrimitives from '@radix-ui/react-dialog';
import classNames from 'classnames';
import type { ReactNode } from 'react';
import type { Intent } from '../../utils/intent';
import { getIntentShadow } from '../../utils/intent';
import { getIntentShadow, getIntentBorder } from '../../utils/intent';
import { Icon } from '../icon';
interface DialogProps {
@ -30,6 +30,7 @@ export function Dialog({
// Need to apply background and text colors again as content is rendered in a portal
'dark:bg-black dark:text-white-95 bg-white text-black-95',
getIntentShadow(intent),
getIntentBorder(intent),
contentClassNames
);
return (
@ -41,10 +42,13 @@ export function Dialog({
/>
<DialogPrimitives.Content className={contentClasses}>
<DialogPrimitives.Close
className="p-12 absolute top-0 right-0"
className="p-2 absolute top-8 right-8 leading-[0] focus:outline-none focus-visible:outline-none focus-visible:border focus-visible:border-vega-yellow"
data-testid="dialog-close"
>
<Icon name="cross" />
<Icon
name="cross"
className="focus:outline-none focus-visible:outline-none"
/>
</DialogPrimitives.Close>
{title && (
<h1

View File

@ -6,13 +6,11 @@ import {
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuItemIndicator,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from './dropdown-menu';
import { Button } from '../button';
import { Icon } from '../icon';
export default {
@ -31,20 +29,16 @@ export const CheckboxItems = () => {
return (
<div style={{ textAlign: 'center', padding: 50 }}>
<DropdownMenu>
<DropdownMenuTrigger>
<Button appendIconName="chevron-down">Options</Button>
<DropdownMenuTrigger className="w-[300px]">
<span>Select many things</span>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuContent className="w-[300px]">
{checkboxItems.map(({ label, state: [checked, setChecked] }) => (
<DropdownMenuCheckboxItem
key={label}
inset
checked={checked}
onCheckedChange={setChecked}
>
<DropdownMenuItemIndicator>
<Icon name="tick" />
</DropdownMenuItemIndicator>
{label}
</DropdownMenuCheckboxItem>
))}
@ -56,13 +50,13 @@ export const CheckboxItems = () => {
export const RadioItems = () => {
const files = ['README.md', 'index.js', 'page.css'];
const [file, setFile] = useState(files[1]);
const [selected, setSelected] = useState(files[1]);
return (
<div style={{ textAlign: 'center', padding: 50 }}>
<DropdownMenu>
<DropdownMenuTrigger>
<Button appendIconName="chevron-down">Open</Button>
<span>Open</span>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem inset onSelect={() => console.log('minimize')}>
@ -75,19 +69,38 @@ export const RadioItems = () => {
Smaller
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuRadioGroup value={file} onValueChange={setFile}>
<DropdownMenuRadioGroup value={selected} onValueChange={setSelected}>
{files.map((file) => (
<DropdownMenuRadioItem key={file} inset value={file}>
{file}
<DropdownMenuItemIndicator>
<Icon name="tick" />
</DropdownMenuItemIndicator>
</DropdownMenuRadioItem>
))}
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
<p>Selected file: {file}</p>
<p>Selected file: {selected}</p>
</div>
);
};
export const IconMenu = () => {
const iconMenuItems = [
{ label: 'IconMenu Item 1' },
{ label: 'IconMenu Item 2' },
];
return (
<div style={{ textAlign: 'center', padding: 50 }}>
<DropdownMenu>
<DropdownMenuTrigger>
<Icon name="cog" />
</DropdownMenuTrigger>
<DropdownMenuContent>
{iconMenuItems.map(({ label }) => (
<DropdownMenuItem key={label}>{label}</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
);
};

View File

@ -1,34 +1,31 @@
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
import classNames from 'classnames';
import { forwardRef } from 'react';
import { Button } from '../button';
const itemStyles = classNames([
'text-ui',
'text-black',
'dark:text-white',
'flex',
'items-center',
'justify-between',
'leading-1',
const itemClass = classNames(
'relative',
'flex items-center justify-between',
'text-ui leading-1',
'h-[25px]',
'py-0 pr-8',
'cursor-default',
'hover:cursor-pointer',
'select-none',
'whitespace-nowrap',
'h-[25px]',
'py-0',
'pr-8',
'color-black',
]);
'focus:bg-vega-pink dark:focus:bg-vega-yellow',
'focus:text-white dark:focus:text-black',
'focus:outline-none'
);
const itemClass = classNames(itemStyles, [
'focus:bg-vega-yellow',
'dark:focus:bg-vega-yellow',
'focus:text-black',
'dark:focus:text-black',
'focus:outline-none',
]);
function getItemClasses(inset: boolean) {
return classNames(itemClass, inset ? 'pl-28' : 'pl-4', 'relative');
function getItemClasses(inset: boolean, checked?: boolean) {
return classNames(
itemClass,
inset ? 'pl-28' : 'pl-8',
checked
? 'bg-vega-pink dark:bg-vega-yellow text-white dark:text-black'
: 'text-black dark:text-white'
);
}
/**
@ -40,7 +37,25 @@ export const DropdownMenu = DropdownMenuPrimitive.Root;
* The button that toggles the dropdown menu.
* By default, the {@link DropdownMenuContent} will position itself against the trigger.
*/
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
export const DropdownMenuTrigger = forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Trigger>,
React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>
>(({ children, className }, forwardedRef) => (
<DropdownMenuPrimitive.Trigger
asChild={true}
ref={forwardedRef}
className="focus-visible:outline-none focus-visible:shadow-inset-vega-pink dark:focus-visible:shadow-inset-vega-yellow"
>
<Button
variant="secondary"
appendIconName="chevron-down"
boxShadow={false}
className={classNames(className, 'justify-between px-8 font-normal')}
>
{children}
</Button>
</DropdownMenuPrimitive.Trigger>
));
/**
* Used to group multiple {@link DropdownMenuRadioItem}s.
@ -58,7 +73,7 @@ export const DropdownMenuContent = forwardRef<
{...contentProps}
ref={forwardedRef}
className={classNames(
'inline-block box-border border-1 border-black bg-white dark:bg-black p-4',
'inline-block box-border border-1 border-black bg-white dark:bg-black-60',
className
)}
/>
@ -92,7 +107,12 @@ export const DropdownMenuCheckboxItem = forwardRef<
<DropdownMenuPrimitive.CheckboxItem
{...checkboxItemProps}
ref={forwardedRef}
className={classNames(getItemClasses(inset), className)}
className={classNames(
getItemClasses(inset, checkboxItemProps.checked),
className,
'hover:shadow-inset-black dark:hover:shadow-inset-white',
'focus:shadow-inset-black dark:focus:shadow-inset-white'
)}
/>
));

View File

@ -1,32 +1,49 @@
import classNames from 'classnames';
import type { ReactNode } from 'react';
import classnames from 'classnames';
interface FormGroupProps {
children: ReactNode;
label?: string;
labelFor?: string;
labelAlign?: 'left' | 'right';
labelDescription?: string;
className?: string;
hasError?: boolean;
}
export const FormGroup = ({
children,
label,
labelFor,
labelDescription,
labelAlign = 'left',
className,
hasError,
}: FormGroupProps) => {
const labelClasses = classNames('block text-ui mb-4', {
'text-right': labelAlign === 'right',
});
return (
<div
data-testid="form-group"
className={className?.includes('mb') ? className : `${className} mb-20`}
className={classnames(className, { 'mb-20': !className?.includes('mb') })}
>
{label && (
<label className={labelClasses} htmlFor={labelFor}>
{label}
<label htmlFor={labelFor}>
<div
className={classNames(
'mb-4 text-body-large text-black dark:text-white',
{
'border-l-4 border-danger pl-8': hasError,
'text-right': labelAlign === 'right',
}
)}
>
<div className="font-bold mb-2">{label}</div>
{labelDescription && (
<div className={classNames({ 'text-danger': hasError })}>
{labelDescription}
</div>
)}
</div>
</label>
)}
{children}

View File

@ -11,20 +11,48 @@ export default {
labelFor: {
type: 'string',
},
labelDescription: {
type: 'string',
},
className: {
type: 'string',
},
hasError: {
type: 'boolean',
},
},
} as Meta;
const Template: Story = (args) => (
<FormGroup {...args} label="label" labelFor="test">
<FormGroup {...args}>
<Input id="labelFor" />
</FormGroup>
);
export const Default = Template.bind({});
Default.args = {
label: 'label',
label: 'Label',
labelFor: 'labelFor',
};
export const Error = Template.bind({});
Error.args = {
label: 'Label',
labelFor: 'labelFor',
hasError: true,
};
export const WithDescription = Template.bind({});
WithDescription.args = {
label: 'Label',
labelFor: 'labelFor',
labelDescription: 'with description text',
};
export const WithDescriptionAndError = Template.bind({});
WithDescriptionAndError.args = {
hasError: true,
label: 'Label',
labelFor: 'labelFor',
labelDescription: 'with description text',
};

View File

@ -1 +0,0 @@
export * from './grid-tabs';

View File

@ -6,10 +6,10 @@ export * from './button';
export * from './callout';
export * from './card';
export * from './copy-with-tooltip';
export * from './checkbox';
export * from './dialog';
export * from './dropdown-menu';
export * from './form-group';
export * from './grid-tabs';
export * from './icon';
export * from './indicator';
export * from './input';
@ -24,6 +24,7 @@ export * from './select';
export * from './sparkline';
export * from './splash';
export * from './syntax-highlighter';
export * from './tabs';
export * from './text-area';
export * from './theme-switcher';
export * from './toggle';

View File

@ -1,6 +1,6 @@
import classNames from 'classnames';
import { Intent } from '../../utils/intent';
import { getVariantBackground } from '../../utils/intent';
import { getIntentTextAndBackground } from '../../utils/intent';
interface IndicatorProps {
variant?: Intent;
@ -9,7 +9,7 @@ interface IndicatorProps {
export const Indicator = ({ variant = Intent.None }: IndicatorProps) => {
const names = classNames(
'inline-block w-8 h-8 mb-2 mr-8 rounded',
getVariantBackground(variant)
getIntentTextAndBackground(variant)
);
return <div className={names} />;
};

View File

@ -131,17 +131,22 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
const hasPrepended = !!(prependIconName || prependElement);
const hasAppended = !!(appendIconName || appendElement);
const inputClassName = classNames('appearance-none', 'h-28', className, {
'pl-28': hasPrepended,
'pr-28': hasAppended,
'border-vega-pink dark:border-vega-pink': hasError,
});
const inputClassName = classNames(
'appearance-none',
'h-28',
'dark:color-scheme-dark',
className,
{
'pl-28': hasPrepended,
'pr-28': hasAppended,
}
);
const input = (
<input
{...props}
ref={ref}
className={classNames(defaultFormElement, inputClassName)}
className={classNames(defaultFormElement(hasError), inputClassName)}
/>
);

View File

@ -1,6 +1,6 @@
import type { ReactNode } from 'react';
import classNames from 'classnames';
import { getVariantBackground } from '../../utils/intent';
import { getIntentTextAndBackground } from '../../utils/intent';
import { Intent } from '../../utils/intent';
interface LozengeProps {
@ -15,7 +15,7 @@ const getLozengeClasses = (
) => {
return classNames(
['rounded-md', 'font-mono', 'leading-none', 'p-4'],
getVariantBackground(variant),
getIntentTextAndBackground(variant),
className
);
};

View File

@ -40,8 +40,8 @@ const priceChangeClassNames = (value: number | bigint) =>
value === 0
? 'text-black dark:text-white'
: value > 0
? `text-green-dark dark:text-green-vega `
: `text-red-dark dark:text-red-vega`;
? `text-vega-green-dark dark:text-vega-green `
: `text-vega-red-dark dark:text-vega-red`;
export const PriceCellChange = React.memo(
({ candles, decimalPlaces }: PriceChangeCellProps) => {

View File

@ -35,9 +35,8 @@ export const Radio = ({ id, value, label, disabled, hasError }: RadioProps) => {
const itemClasses = classNames(
'flex justify-center items-center',
'w-[17px] h-[17px] rounded-full border',
'focus:outline-0 focus-visible:outline-0',
'focus-visible:shadow-radio focus-visible:shadow-vega-pink dark:focus-visible:shadow-vega-yellow',
'border-black-60 dark:border-white-60',
'focus:outline-none focus-visible:outline-none',
'focus-visible:shadow-vega-pink dark:focus-visible:shadow-vega-yellow',
'dark:bg-white-25',
{
'border-black-60 dark:border-white-60': !hasError,

View File

@ -8,7 +8,9 @@ export default {
const Template: Story = (args) => (
<Select {...args}>
<option value="Only option">Only option</option>
<option value="Option 1">Option 1</option>
<option value="Option 2">Option 2</option>
<option value="Option 3">Option 3</option>
</Select>
);

View File

@ -15,9 +15,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(
<select
ref={ref}
{...props}
className={classNames(defaultFormElement, className, 'h-28', {
'border-vega-pink dark:border-vega-pink': hasError,
})}
className={classNames(defaultFormElement(hasError), className, 'h-28')}
/>
)
);

View File

@ -36,7 +36,7 @@ it('Renders a red line if the last value is less than the first', () => {
const paths = screen.getAllByTestId('sparkline-path');
const path = paths[0];
expect(path).toHaveClass(
'[vector-effect:non-scaling-stroke] stroke-red-dark dark:stroke-red'
'[vector-effect:non-scaling-stroke] stroke-vega-red-dark dark:stroke-vega-red'
);
});
@ -48,7 +48,7 @@ it('Renders a green line if the last value is greater than the first', () => {
const paths = screen.getAllByTestId('sparkline-path');
const path = paths[0];
expect(path).toHaveClass(
'[vector-effect:non-scaling-stroke] stroke-green-dark dark:stroke-green'
'[vector-effect:non-scaling-stroke] stroke-vega-green-dark dark:stroke-vega-green'
);
});

View File

@ -8,8 +8,8 @@ function colorByChange(a: number, b: number) {
return a === b
? 'stroke-black/40 dark:stroke-white/40'
: a < b
? 'stroke-green-dark dark:stroke-green'
: 'stroke-red-dark dark:stroke-red';
? 'stroke-vega-green-dark dark:stroke-vega-green'
: 'stroke-vega-red-dark dark:stroke-vega-red';
}
export interface SparklineProps {

View File

@ -0,0 +1 @@
export * from './tabs';

View File

@ -0,0 +1,33 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Tabs, Tab } from './tabs';
const renderComponent = (
<Tabs>
<Tab id="one" name="Tab one">
<p>Tab one content</p>
</Tab>
<Tab id="two" name="Tab two">
<p>Tab two content</p>
</Tab>
<Tab id="three" name="Tab three">
<p>Tab three content</p>
</Tab>
</Tabs>
);
describe('Tabs', () => {
it('should render tabs successfully', () => {
render(renderComponent);
expect(screen.getByTestId('Tab one')).toBeInTheDocument();
expect(screen.getByTestId('Tab two')).toBeInTheDocument();
expect(screen.getByTestId('Tab three')).toBeInTheDocument();
});
it('shows tabs display the correct content when clicked', async () => {
render(renderComponent);
expect(screen.getByText('Tab one content')).toBeInTheDocument();
await userEvent.click(screen.getByText('Tab two'));
expect(await screen.getByText('Tab two content')).toBeInTheDocument();
});
});

View File

@ -0,0 +1,21 @@
import type { Story, Meta } from '@storybook/react';
import { Tabs, Tab } from './tabs';
export default {
component: Tabs,
title: 'Tabs',
} as Meta;
export const Default: Story = () => (
<Tabs>
<Tab id="one" name="Tab one">
<p>Tab one content</p>
</Tab>
<Tab id="two" name="Tab two">
<p>Tab two content</p>
</Tab>
<Tab id="three" name="Tab three">
<p>Tab three content</p>
</Tab>
</Tabs>
);

View File

@ -1,73 +1,79 @@
import * as Tabs from '@radix-ui/react-tabs';
import * as TabsPrimitive from '@radix-ui/react-tabs';
import classNames from 'classnames';
import type { ReactElement, ReactNode } from 'react';
import { Children, isValidElement, useState } from 'react';
interface GridTabsProps {
children: ReactElement<GridTabProps>[];
interface TabsProps {
children: ReactElement<TabProps>[];
}
export const GridTabs = ({ children }: GridTabsProps) => {
export const Tabs = ({ children }: TabsProps) => {
const [activeTab, setActiveTab] = useState<string>(() => {
return children[0].props.id;
});
return (
<Tabs.Root
<TabsPrimitive.Root
value={activeTab}
className="h-full grid grid-rows-[min-content_1fr]"
onValueChange={(value) => setActiveTab(value)}
>
<div className="bg-black-10 dark:bg-white-10">
<Tabs.List
<TabsPrimitive.List
className="flex flex-nowrap gap-4 overflow-x-auto"
role="tablist"
>
{Children.map(children, (child) => {
if (!isValidElement(child)) return null;
const isActive = child.props.id === activeTab;
const triggerClass = classNames('py-4', 'px-12', 'capitalize', {
'text-black dark:text-vega-yellow': isActive,
'bg-white dark:bg-black': isActive,
'text-black-60 dark:text-white-60': !isActive,
'bg-black-10 dark:bg-white-10': !isActive,
});
const triggerClass = classNames(
'py-4 px-20',
'capitalize',
'focus-visible:outline-none focus-visible:shadow-inset-vega-pink dark:focus-visible:shadow-inset-vega-yellow',
{
'font-semibold text-vega-pink dark:text-vega-yellow': isActive,
'bg-white dark:bg-black': isActive,
'text-black dark:text-white': !isActive,
'bg-white-90 dark:bg-black-70 hover:bg-white-95 dark:hover:bg-black-80':
!isActive,
}
);
return (
<Tabs.Trigger
<TabsPrimitive.Trigger
data-testid={child.props.name}
value={child.props.id}
className={triggerClass}
>
{child.props.name}
</Tabs.Trigger>
</TabsPrimitive.Trigger>
);
})}
</Tabs.List>
</TabsPrimitive.List>
</div>
<div className="h-full overflow-auto">
{Children.map(children, (child) => {
if (!isValidElement(child)) return null;
return (
<Tabs.Content
<TabsPrimitive.Content
value={child.props.id}
className="h-full"
data-testid={`tab-${child.props.id}`}
>
{child.props.children}
</Tabs.Content>
</TabsPrimitive.Content>
);
})}
</div>
</Tabs.Root>
</TabsPrimitive.Root>
);
};
interface GridTabProps {
interface TabProps {
children: ReactNode;
id: string;
name: string;
}
export const GridTab = ({ id, children }: GridTabProps) => {
return <div>{children}</div>;
export const Tab = ({ children, ...props }: TabProps) => {
return <div {...props}>{children}</div>;
};

View File

@ -11,9 +11,7 @@ export interface TextAreaProps
export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
({ className, hasError, ...props }, ref) => {
const classes = classNames(defaultFormElement, className, {
'border-vega-pink dark:border-vega-pink': hasError,
});
const classes = classNames(defaultFormElement(hasError), className);
return <textarea {...props} ref={ref} className={classes} />;
}
);

View File

@ -34,9 +34,10 @@ export const Toggle = ({
'border border-black-60 active:border-black dark:border-white-60 dark:active:border-white peer-checked:border-black dark:peer-checked:border-vega-yellow',
'group-first-of-type:rounded-tl group-first-of-type:rounded-bl group-last-of-type:rounded-tr group-last-of-type:rounded-br',
'px-28 py-4',
'peer-checked:bg-vega-yellow hover:bg-black-25 dark:hover:bg-white-25 hover:peer-checked:bg-vega-yellow',
'text-ui text-black-60 dark:text-white-60 peer-checked:text-black active:text-black dark:active:text-white peer-checked:font-bold text-center',
'cursor-pointer peer-checked:cursor-auto select-none transition-all'
'peer-checked:bg-vega-pink dark:peer-checked:bg-vega-yellow hover:bg-black-10 dark:hover:bg-white-25 hover:peer-checked:bg-vega-pink dark:hover:peer-checked:bg-vega-yellow focus-visible:bg-black-10 dark:focus-visible:bg-white-25',
'text-ui text-center peer-checked:font-bold peer-checked:text-white dark:peer-checked:text-black text-black-60 dark:text-white-60 active:text-black dark:active:text-white focus-visible:text-black dark:focus-visible:text-white',
'focus-within:shadow-inset-black',
'cursor-pointer peer-checked:cursor-auto select-none transition-all duration-75'
);
return (

View File

@ -11,7 +11,7 @@ const Template: Story<TooltipProps> = (args) => <Tooltip {...args} />;
export const Uncontrolled = Template.bind({});
Uncontrolled.args = {
children: <button>Click me!</button>,
children: <button>Hover on me!</button>,
description: 'Tooltip content!',
};

View File

@ -21,15 +21,19 @@ export const Tooltip = ({ children, description, open, align }: TooltipProps) =>
<Root open={open}>
<Trigger asChild>{children}</Trigger>
<Content align={align} alignOffset={5}>
<div className="relative z-0 p-8 bg-black-50 border border-black-60 text-white rounded-sm max-w-sm">
{description}
</div>
<Arrow
width={10}
height={5}
offset={10}
className="fill-vega-green"
className="z-[1] fill-black-60 dark:fill-white-60 z-0 translate-x-[1px] translate-y-[-1px]"
/>
<Arrow
width={8}
height={4}
className="z-[1] translate-y-[-1px] fill-black-50"
/>
<div className="px-12 py-8 bg-vega-green text-black rounded-sm max-w-sm">
{description}
</div>
</Content>
</Root>
</Provider>

View File

@ -30,11 +30,28 @@ full colour palette [here](https://tailwindcss.com/docs/customizing-colors/#defa
subtitle="Black"
colors={theme.colors.black}
/>
<ColorItem
title="theme.color.blue"
subtitle="Blue"
colors={[theme.colors.blue]}
/>
<ColorItem
title="theme.color.coral"
subtitle="Coral"
colors={[theme.colors.coral]}
/>
<ColorItem
title="theme.color.orange"
subtitle="Orange"
colors={[theme.colors.orange]}
/>
<ColorPalette>
<ColorItem
title="theme.color.selected"
subtitle="Selected"
colors={[theme.colors.selected]}
/>
</ColorPalette>
</ColorPalette>
### Vega
@ -51,13 +68,23 @@ full colour palette [here](https://tailwindcss.com/docs/customizing-colors/#defa
<ColorPalette>
<ColorItem
title="theme.color.intent"
subtitle="Intent"
colors={{
success: theme.colors.success,
warning: theme.colors.warning,
danger: theme.colors.danger,
}}
title="theme.color.danger"
subtitle="danger"
colors={[theme.colors.danger]}
/>
</ColorPalette>
<ColorPalette>
<ColorItem
title="theme.color.warning"
subtitle="warning"
colors={[theme.colors.warning]}
/>
</ColorPalette>
<ColorPalette>
<ColorItem
title="theme.color.success"
subtitle="success"
colors={[theme.colors.success]}
/>
</ColorPalette>

View File

@ -1,30 +1,40 @@
export enum Intent {
None,
Primary,
Success,
Warning,
Danger,
Warning,
Success,
}
export const getIntentShadow = (intent?: Intent) => {
export const getIntentShadow = (intent = Intent.None) => {
return {
'shadow-callout': true,
'shadow-intent': true,
'shadow-danger': intent === Intent.Danger,
'shadow-warning': intent === Intent.Warning,
'shadow-black dark:shadow-white':
intent === Intent.None || intent === Intent.Primary,
'shadow-success': intent === Intent.Success,
'shadow-black dark:shadow-white': intent === Intent.None,
'shadow-vega-pink dark:shadow-vega-yellow': intent === Intent.Primary,
};
};
export const getVariantBackground = (variant?: Intent) => {
export const getIntentBorder = (intent = Intent.None) => {
return {
'bg-black text-white dark:bg-white dark:text-black':
variant === Intent.None,
'bg-vega-pink text-black dark:bg-vega-yellow dark:text-black-normal':
variant === Intent.Primary,
'bg-danger text-white': variant === Intent.Danger,
'bg-warning text-black': variant === Intent.Warning,
'bg-success text-black': variant === Intent.Success,
border: true,
'border-danger': intent === Intent.Danger,
'border-warning': intent === Intent.Warning,
'border-black dark:border-white':
intent === Intent.None || intent === Intent.Primary,
'border-success': intent === Intent.Success,
};
};
export const getIntentTextAndBackground = (intent = Intent.None) => {
return {
'bg-black text-white dark:bg-white text-black': intent === Intent.None,
'bg-vega-pink text-black dark:bg-vega-yellow dark:text-black-normal':
intent === Intent.Primary,
'bg-danger text-white': intent === Intent.Danger,
'bg-warning text-black': intent === Intent.Warning,
'bg-success text-black': intent === Intent.Success,
};
};

View File

@ -1,14 +1,22 @@
export const defaultFormElement = [
'flex items-center w-full',
'box-border',
'border rounded-none',
'bg-clip-padding',
'border-black-60 dark:border-white-60',
'bg-black-25 dark:bg-white-25',
'text-black placeholder:text-black-60 dark:text-white dark:placeholder:text-white-60',
'text-ui',
'px-8',
'focus-visible:shadow-focus dark:focus-visible:shadow-focus-dark',
'focus-visible:outline-0',
'disabled:bg-black-10 disabled:dark:bg-white-10',
];
import classnames from 'classnames';
export const defaultFormElement = (hasError?: boolean) =>
classnames(
'flex items-center w-full',
'box-border',
'border rounded-none',
'bg-clip-padding',
'shadow-input dark:shadow-input-dark',
'bg-white dark:bg-white-25',
'text-black placeholder:text-black-60 dark:text-white dark:placeholder:text-white-60',
'text-ui',
'px-8',
'focus-visible:outline-none',
'disabled:bg-black-10 disabled:dark:bg-white-10',
{
'input-border dark:input-border-dark focus-visible:shadow-input-focus dark:focus-visible:shadow-input-focus-dark':
!hasError,
'border-vega-red focus:shadow-input-focus-error dark:focus:shadow-input-focus-error-dark':
hasError,
}
);

View File

@ -23,7 +23,7 @@ export const VegaManageDialog = ({
title={t('SELECT A VEGA KEY')}
open={dialogOpen}
onChange={setDialogOpen}
intent={Intent.Warning}
intent={Intent.Primary}
>
<div className="text-ui">
{keypairs ? (

View File

@ -22,7 +22,7 @@ export const Web3ConnectDialog = ({
<Dialog
open={dialogOpen}
onChange={setDialogOpen}
intent={Intent.Warning}
intent={Intent.Primary}
title={t('Connect to your Ethereum wallet')}
>
<ul data-testid="web3-connector-list">

View File

@ -24,12 +24,14 @@
"@radix-ui/react-dropdown-menu": "^0.1.6",
"@radix-ui/react-icons": "^1.1.1",
"@radix-ui/react-radio-group": "^0.1.5",
"@radix-ui/react-select": "^0.1.1",
"@radix-ui/react-tabs": "^0.1.5",
"@radix-ui/react-tooltip": "^0.1.7",
"@sentry/nextjs": "^6.19.3",
"@sentry/react": "^6.19.2",
"@sentry/tracing": "^6.19.2",
"@vegaprotocol/vegawallet-service-api-client": "^0.4.13",
"@testing-library/user-event": "^14.2.1",
"@walletconnect/ethereum-provider": "^1.7.5",
"@web3-react/core": "8.0.20-beta.0",
"@web3-react/metamask": "8.0.16-beta.0",

View File

@ -3349,6 +3349,13 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64"
integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==
"@radix-ui/number@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-0.1.0.tgz#73ad13d5cc5f75fa5e147d72e5d5d5e50d688256"
integrity sha512-rpf6QiOWLHAkM4FEMYu9i+5Jr8cKT893+R4mPpcdsy4LD7omr9JfdOqj/h/xPA5+EcVrpMMlU6rrRYpUB5UI8g==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/popper@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/popper/-/popper-0.1.0.tgz#c387a38f31b7799e1ea0d2bb1ca0c91c2931b063"
@ -3613,6 +3620,31 @@
"@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-use-controllable-state" "0.1.0"
"@radix-ui/react-select@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-0.1.1.tgz#ceedea6856a37e4079492e1c69601797cedd2c85"
integrity sha512-xY3jLz2c6ZBHflldGsA79bE/swUfRMpiRPQf+JDihOufsd3z5uW22PFe0MS5bGZiIpK6aAZAvwqEd2Bu7hqp8w==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/number" "0.1.0"
"@radix-ui/primitive" "0.1.0"
"@radix-ui/react-collection" "0.1.4"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-context" "0.1.1"
"@radix-ui/react-dismissable-layer" "0.1.5"
"@radix-ui/react-focus-scope" "0.1.4"
"@radix-ui/react-id" "0.1.5"
"@radix-ui/react-label" "0.1.5"
"@radix-ui/react-portal" "0.1.4"
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-use-controllable-state" "0.1.0"
"@radix-ui/react-use-layout-effect" "0.1.0"
"@radix-ui/react-use-previous" "0.1.1"
"@radix-ui/react-visually-hidden" "0.1.4"
aria-hidden "^1.1.1"
react-remove-scroll "^2.4.0"
"@radix-ui/react-slot@0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-0.1.2.tgz#e6f7ad9caa8ce81cc8d532c854c56f9b8b6307c8"
@ -5816,6 +5848,11 @@
"@babel/runtime" "^7.12.5"
"@testing-library/dom" "^8.0.0"
"@testing-library/user-event@^14.2.1":
version "14.2.1"
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.2.1.tgz#8c5ff2d004544bb2220e1d864f7267fe7eb6c556"
integrity sha512-HOr1QiODrq+0j9lKU5i10y9TbhxMBMRMGimNx10asdmau9cb8Xb1Vyg0GvTwyIL2ziQyh2kAloOtAQFBQVuecA==
"@tootallnate/once@1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"