Merge branch 'master' into task/token-flow-tests
This commit is contained in:
commit
ef6b79e1f8
apps
explorer/src/app/components
simple-trading-app/src/app/components
static/src/assets
token-e2e
token
.env.env.capsuleREADME.md
src
components
config
hooks
i18n/translations
index.htmllib
routes
claim
contracts
governance/components
current-proposal-status
vote-details
redemption
staking
tranches
trading-e2e/src/support/mocks
trading
libs
candles-chart/src/lib
deal-ticket/src/components
deposits/src/lib
smart-contracts/src
contracts
utils
tailwindcss-config/src
trades/src/lib
ui-toolkit/src
components
ag-grid
button
callout
checkbox
dialog
dropdown-menu
form-group
grid-tabs
index.tsindicator
input
lozenge
price-change
radio-group
select
sparkline
tabs
text-area
toggle
tooltip
vega-logo
primitives
utils
wallet/src
web3/src/lib
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
|
@ -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}
|
||||
>
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
17
apps/token/.env.capsule
Normal 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
|
@ -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:
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -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} />
|
||||
|
@ -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(
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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]
|
||||
);
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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" />
|
||||
|
150
apps/token/src/lib/url-connector.ts
Normal file
150
apps/token/src/lib/url-connector.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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][];
|
||||
};
|
||||
|
@ -25,7 +25,7 @@ export const TargetAddressMismatch = ({
|
||||
}}
|
||||
components={{
|
||||
bold: <strong />,
|
||||
red: <span className={'text-red'} />,
|
||||
red: <span className={'text-vega-red'} />,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
|
@ -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>
|
||||
|
@ -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> {daysClosedAgo}.</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (!majorityMet) {
|
||||
return (
|
||||
<>
|
||||
<span>{t('voteFailedReason')}</span>
|
||||
<StatusFail>{t('majorityNotMet')}</StatusFail>
|
||||
<span> {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> {daysClosedAgo}.</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (!majorityMet) {
|
||||
return (
|
||||
<>
|
||||
<span>{t('voteFailedReason')}</span>
|
||||
<StatusFail>{t('majorityNotMet')}</StatusFail>
|
||||
<span> {daysClosedAgo}.</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span>{t('voteFailedReason')}</span>
|
||||
|
@ -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) {
|
||||
|
@ -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>;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
|
||||
<span>
|
||||
<CurrentProposalStatus proposal={proposal} />
|
||||
</span>
|
||||
.
|
||||
{'. '}
|
||||
{proposal.state === ProposalState.Open ? daysLeft : null}
|
||||
</p>
|
||||
<table className="w-full font-normal mb-12">
|
||||
|
@ -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 (
|
||||
|
@ -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">
|
||||
|
@ -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(() => {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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">
|
||||
|
@ -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}
|
||||
|
@ -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: {
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
</>
|
||||
|
@ -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>
|
||||
|
@ -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) => (
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
});
|
||||
});
|
||||
|
@ -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,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -1,6 +0,0 @@
|
||||
import { hexadecimalify } from './hexadecimalify';
|
||||
|
||||
test('Prepends strings with 0x', () => {
|
||||
expect(hexadecimalify('abc')).toEqual('0xabc');
|
||||
expect(hexadecimalify('123456789')).toEqual('0x123456789');
|
||||
});
|
@ -1,3 +0,0 @@
|
||||
export function hexadecimalify(str: string) {
|
||||
return `0x${str}`;
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
export * from './ascii-to-hex';
|
||||
export * from './hexadecimalify';
|
||||
export * from './prepend-0x';
|
||||
|
6
libs/smart-contracts/src/utils/prepend-0x.test.ts
Normal file
6
libs/smart-contracts/src/utils/prepend-0x.test.ts
Normal 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');
|
||||
});
|
3
libs/smart-contracts/src/utils/prepend-0x.ts
Normal file
3
libs/smart-contracts/src/utils/prepend-0x.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function prepend0x(str: string) {
|
||||
return `0x${str}`;
|
||||
}
|
@ -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')",
|
||||
},
|
||||
};
|
||||
|
@ -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',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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) =>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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',
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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'
|
||||
);
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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}`
|
||||
|
64
libs/ui-toolkit/src/components/checkbox/checkbox.spec.tsx
Normal file
64
libs/ui-toolkit/src/components/checkbox/checkbox.spec.tsx
Normal 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);
|
||||
});
|
||||
});
|
45
libs/ui-toolkit/src/components/checkbox/checkbox.stories.tsx
Normal file
45
libs/ui-toolkit/src/components/checkbox/checkbox.stories.tsx
Normal 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 />
|
||||
);
|
81
libs/ui-toolkit/src/components/checkbox/checkbox.tsx
Normal file
81
libs/ui-toolkit/src/components/checkbox/checkbox.tsx
Normal 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>
|
||||
);
|
||||
};
|
1
libs/ui-toolkit/src/components/checkbox/index.ts
Normal file
1
libs/ui-toolkit/src/components/checkbox/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './checkbox';
|
@ -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,
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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'
|
||||
)}
|
||||
/>
|
||||
));
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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',
|
||||
};
|
||||
|
@ -1 +0,0 @@
|
||||
export * from './grid-tabs';
|
@ -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';
|
||||
|
@ -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} />;
|
||||
};
|
||||
|
@ -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)}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -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
|
||||
);
|
||||
};
|
||||
|
@ -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) => {
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
);
|
||||
|
||||
|
@ -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')}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
@ -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'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -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 {
|
||||
|
1
libs/ui-toolkit/src/components/tabs/index.ts
Normal file
1
libs/ui-toolkit/src/components/tabs/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './tabs';
|
33
libs/ui-toolkit/src/components/tabs/tabs.spec.tsx
Normal file
33
libs/ui-toolkit/src/components/tabs/tabs.spec.tsx
Normal 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();
|
||||
});
|
||||
});
|
21
libs/ui-toolkit/src/components/tabs/tabs.stories.tsx
Normal file
21
libs/ui-toolkit/src/components/tabs/tabs.stories.tsx
Normal 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>
|
||||
);
|
@ -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>;
|
||||
};
|
@ -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} />;
|
||||
}
|
||||
);
|
||||
|
@ -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 (
|
||||
|
@ -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!',
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
@ -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,
|
||||
}
|
||||
);
|
||||
|
@ -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 ? (
|
||||
|
@ -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">
|
||||
|
@ -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",
|
||||
|
37
yarn.lock
37
yarn.lock
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user