diff --git a/.prettierignore b/.prettierignore index eb82b9e9f..5967ba713 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,3 +3,4 @@ /dist /coverage __generated__ +*.md diff --git a/README.md b/README.md index e2026c8c8..c427fcded 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ The trading interface built based on a component toolkit. It will provide a way ### [UI toolkit](https://github.com/vegaprotocol/frontend-monorepo/tree/master/libs/ui-toolkit) The UI toolkit contains a set of components used to build interfaces that can interact with the Vega protocol, and follow the design style of the project. +It contains a storybook that can be served with `yarn nx run ui-toolkit:storybook`. ### [Tailwind CSS config](https://github.com/vegaprotocol/frontend-monorepo/tree/master/libs/tailwindcss-config) @@ -30,7 +31,7 @@ The Tailwind CSS config contains theme that align default config with Vega desig Check you have the correct version of Node. You can [install NVM to switch between node versions](https://github.com/nvm-sh/nvm#installing-and-updating). Then `NVM install`. Before you build you will need to `yarn install` in the root directory. -The repository includes a number of template .env files for different networks. Copy from these to the .env file before `serve` to lauch app with different network. +The repository includes a number of template .env files for different networks. Copy from these to the .env file before `serve` to launch app with different network. You can serve any application with `yarn nx run :serve`. ### Build @@ -40,7 +41,9 @@ Run `nx serve my-app` for a dev server. Navigate to http://localhost:4200/. The ### Running tests -Run `nx test my-app` to execute the unit tests with [Jest](https://jestjs.io), or `nx affected:test` to execute just unit tests affected by a change. +Run `yarn nx run -e2e:e2e` to execute the e2e tests with [cypress](https://docs.cypress.io/). You can use the `--watch` flag to open the cypress tests UI in watch mode, see [cypress executor](https://nx.dev/packages/cypress/executors/cypress) for all CLI flags. + +Run `nx test my-app` to execute the unit tests with [Jest](https://jestjs.io), or `nx affected:test` to execute just unit tests affected by a change. You can also use `--watch` with these test to run jest in watch mode, see [Jest executor](https://nx.dev/packages/jest/executors/jest) for all CLI flags. Similarly `nx e2e my-app` will execute the end-to-end tests with [Cypress](https://www.cypress.io)., and `nx affected:e2e` will execute just the end-to-end tests affected by a change. @@ -59,6 +62,13 @@ Follow the following steps to start using a local network with the Vega Explorer 1. Start the explorer frontend application with the `.env.vegacapsule` env file 1. Go to [http://localhost:3000](http://localhost:3000) in your browser +If you simply want to run Explorer locally, without using a local network: + +```bash +cd apps/explorer && cp .env.testnet .env.local +yarn nx run explorer:serve +``` + # 📑 License [MIT](./LICENSE) diff --git a/apps/token/src/components/key-value-table/index.tsx b/apps/token/src/components/key-value-table/index.tsx deleted file mode 100644 index 15ce6e5b3..000000000 --- a/apps/token/src/components/key-value-table/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { KeyValueTable, KeyValueTableRow } from './key-value-table'; diff --git a/apps/token/src/components/key-value-table/key-value-table.scss b/apps/token/src/components/key-value-table/key-value-table.scss deleted file mode 100644 index 7fc8c28d9..000000000 --- a/apps/token/src/components/key-value-table/key-value-table.scss +++ /dev/null @@ -1,85 +0,0 @@ -@import '../../styles/colors'; -@import '../../styles/fonts'; - -$ns: 'key-value-table'; - -.#{$ns}__header, -.#{$ns}__footer { - margin: 10px 0 5px; -} - -.#{$ns}__header { - font-size: 16px; - font-weight: 500; - margin-bottom: 7px; -} - -.#{$ns}__row { - @media (max-width: 640px) { - display: flex; - flex-direction: column; - } -} - -.#{$ns} { - width: 100%; - border-collapse: collapse; - border-spacing: 0; - margin-bottom: 10px; - word-break: break-all; - - tr { - border-bottom: 1px solid $white; - - &:first-child { - border-top: 1px solid $white; - } - } - - th { - word-break: break-word; - text-align: left; - font-weight: 500; - color: $white; - text-transform: uppercase; - } - - th, - td { - vertical-align: top; - padding: 5px; - } - - td { - color: $text-color; - text-align: right; - } - - .bp3-tag { - margin: 0 5px 5px 0; - } - - &.#{$ns}--numerical { - td { - font-family: $font-mono; - - & button { - font-family: $font-main; - } - } - } - - &.#{$ns}--muted { - tr { - border-color: $gray1; - - &:first-child { - border-top: none; - } - - &:last-child { - border-bottom: none; - } - } - } -} diff --git a/apps/token/src/components/key-value-table/key-value-table.tsx b/apps/token/src/components/key-value-table/key-value-table.tsx deleted file mode 100644 index dcad848cd..000000000 --- a/apps/token/src/components/key-value-table/key-value-table.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import './key-value-table.scss'; - -import * as React from 'react'; - -export interface KeyValueTableProps - extends React.HTMLAttributes { - title?: string; - numerical?: boolean; // makes all values monospace - children: React.ReactNode; - muted?: boolean; -} - -export const KeyValueTable = ({ - title, - numerical, - children, - muted, - className, - ...rest -}: KeyValueTableProps) => { - return ( - - {title &&

{title}

} - - {children} -
-
- ); -}; - -export interface KeyValueTableRowProps - extends React.HTMLAttributes { - children: [React.ReactNode, React.ReactNode]; - className?: string; -} - -export const KeyValueTableRow = ({ - children, - className, - ...rest -}: KeyValueTableRowProps) => { - return ( - - {children[0]} - {children[1]} - - ); -}; diff --git a/apps/token/src/lib/format-number.ts b/apps/token/src/lib/format-number.ts index 288aa9b56..01270e06a 100644 --- a/apps/token/src/lib/format-number.ts +++ b/apps/token/src/lib/format-number.ts @@ -5,3 +5,9 @@ export const formatNumber = (value: BigNumber, decimals?: number) => { typeof decimals === 'undefined' ? Math.max(value.dp(), 2) : decimals; return value.dp(decimalPlaces).toFormat(decimalPlaces); }; + +export const formatNumberPercentage = (value: BigNumber, decimals?: number) => { + const decimalPlaces = + typeof decimals === 'undefined' ? Math.max(value.dp(), 2) : decimals; + return `${value.dp(decimalPlaces).toFormat(decimalPlaces)}%`; +}; diff --git a/apps/token/src/routes/claim/claim-flow.tsx b/apps/token/src/routes/claim/claim-flow.tsx index f44f9a779..030996904 100644 --- a/apps/token/src/routes/claim/claim-flow.tsx +++ b/apps/token/src/routes/claim/claim-flow.tsx @@ -8,10 +8,7 @@ import { Trans, useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import type { Tranche } from '@vegaprotocol/smart-contracts-sdk'; -import { - KeyValueTable, - KeyValueTableRow, -} from '../../components/key-value-table'; +import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { useContracts } from '../../contexts/contracts/contracts-context'; import { DATE_FORMAT_LONG } from '../../lib/date-formats'; import { formatNumber } from '../../lib/format-number'; @@ -159,37 +156,34 @@ export const ClaimFlow = ({
- {t('Connected Ethereum address')} - {truncateMiddle(address)} + {t('Connected Ethereum address')} + {truncateMiddle(address)} - {t('Amount of VEGA')} - - {state.claimData - ? formatNumber(state.claimData.claim.amount) - : 'None'} - + {t('Amount of VEGA')} + + {state.claimData + ? formatNumber(state.claimData.claim.amount) + : 'None'} - {t('Claim expires')} - - {state.claimData?.claim.expiry - ? format( - state.claimData?.claim.expiry * 1000, - DATE_FORMAT_LONG - ) - : 'No expiry'} - + {t('Claim expires')} + + {state.claimData?.claim.expiry + ? format( + state.claimData?.claim.expiry * 1000, + DATE_FORMAT_LONG + ) + : 'No expiry'} - {t('Starts unlocking')} - - {format(currentTranche.tranche_start, DATE_FORMAT_LONG)} - + {t('Starts unlocking')} + + {format(currentTranche.tranche_start, DATE_FORMAT_LONG)} - {t('Fully unlocked')} - {format(currentTranche.tranche_end, DATE_FORMAT_LONG)} + {t('Fully unlocked')} + {format(currentTranche.tranche_end, DATE_FORMAT_LONG)}
diff --git a/apps/token/src/routes/governance/components/proposal-change-table/proposal-change-table.tsx b/apps/token/src/routes/governance/components/proposal-change-table/proposal-change-table.tsx index b7819ffbd..a6bc7e3e3 100644 --- a/apps/token/src/routes/governance/components/proposal-change-table/proposal-change-table.tsx +++ b/apps/token/src/routes/governance/components/proposal-change-table/proposal-change-table.tsx @@ -1,10 +1,7 @@ import { format, isFuture } from 'date-fns'; import { useTranslation } from 'react-i18next'; -import { - KeyValueTable, - KeyValueTableRow, -} from '../../../../components/key-value-table'; +import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats'; import type { Proposals_proposals } from '../../proposals/__generated__/Proposals'; import { CurrentProposalState } from '../current-proposal-state'; @@ -19,60 +16,50 @@ export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => { const terms = proposal.terms; return ( - + - {t('id')} - {proposal.id} + {t('id')} + {proposal.id} - {t('state')} - - - + {t('state')} + - - {isFuture(new Date(terms.closingDatetime)) - ? t('closesOn') - : t('closedOn')} - - {format(new Date(terms.closingDatetime), DATE_FORMAT_DETAILED)} + {isFuture(new Date(terms.closingDatetime)) + ? t('closesOn') + : t('closedOn')} + {format(new Date(terms.closingDatetime), DATE_FORMAT_DETAILED)} - - {isFuture(new Date(terms.enactmentDatetime)) - ? t('proposedEnactment') - : t('enactedOn')} - - - {format(new Date(terms.enactmentDatetime), DATE_FORMAT_DETAILED)} - + {isFuture(new Date(terms.enactmentDatetime)) + ? t('proposedEnactment') + : t('enactedOn')} + {format(new Date(terms.enactmentDatetime), DATE_FORMAT_DETAILED)} - {t('proposedBy')} - - {proposal.party.id} - + {t('proposedBy')} + {proposal.party.id} - {t('proposedOn')} - {format(new Date(proposal.datetime), DATE_FORMAT_DETAILED)} + {t('proposedOn')} + {format(new Date(proposal.datetime), DATE_FORMAT_DETAILED)} {proposal.rejectionReason ? ( - {t('rejectionReason')} - {proposal.rejectionReason} + {t('rejectionReason')} + {proposal.rejectionReason} ) : null} {proposal.errorDetails ? ( - {t('errorDetails')} - {proposal.errorDetails} + {t('errorDetails')} + {proposal.errorDetails} ) : null} - {t('type')} - {proposal.terms.change.__typename} + {t('type')} + {proposal.terms.change.__typename} ); diff --git a/apps/token/src/routes/governance/components/proposal-votes-table/proposal-votes-table.tsx b/apps/token/src/routes/governance/components/proposal-votes-table/proposal-votes-table.tsx index 5929f8530..e4adddb4e 100644 --- a/apps/token/src/routes/governance/components/proposal-votes-table/proposal-votes-table.tsx +++ b/apps/token/src/routes/governance/components/proposal-votes-table/proposal-votes-table.tsx @@ -1,10 +1,10 @@ import { useTranslation } from 'react-i18next'; +import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { - KeyValueTable, - KeyValueTableRow, -} from '../../../../components/key-value-table'; -import { formatNumber } from '../../../../lib/format-number'; + formatNumber, + formatNumberPercentage, +} from '../../../../lib/format-number'; import { useVoteInformation } from '../../hooks'; import type { Proposals_proposals } from '../../proposals/__generated__/Proposals'; @@ -34,65 +34,65 @@ export const ProposalVotesTable = ({ proposal }: ProposalVotesTableProps) => { return ( - {t('willPass')} - {willPass ? '👍' : '👎'} + {t('willPass')} + {willPass ? '👍' : '👎'} - {t('majorityMet')} - {majorityMet ? '👍' : '👎'} + {t('majorityMet')} + {majorityMet ? '👍' : '👎'} - {t('participationMet')} - {participationMet ? '👍' : '👎'} + {t('participationMet')} + {participationMet ? '👍' : '👎'} - {t('tokenForProposal')} - {formatNumber(yesTokens, 2)} + {t('tokenForProposal')} + {formatNumber(yesTokens, 2)} - {t('tokensAgainstProposal')} - {formatNumber(noTokens, 2)} + {t('tokensAgainstProposal')} + {formatNumber(noTokens, 2)} - {t('participationRequired')} - {formatNumber(requiredParticipation)}% + {t('participationRequired')} + {formatNumberPercentage(requiredParticipation)} - {t('majorityRequired')} - {formatNumber(requiredMajorityPercentage)}% + {t('majorityRequired')} + {formatNumberPercentage(requiredMajorityPercentage)} - {t('numberOfVotingParties')} - {formatNumber(totalVotes, 0)} + {t('numberOfVotingParties')} + {formatNumber(totalVotes, 0)} - {t('totalTokensVotes')} - {formatNumber(totalTokensVoted, 2)} + {t('totalTokensVotes')} + {formatNumber(totalTokensVoted, 2)} - {t('totalTokenVotedPercentage')} - {formatNumber(totalTokensPercentage, 2)}% + {t('totalTokenVotedPercentage')} + {formatNumberPercentage(totalTokensPercentage, 2)} - {t('numberOfForVotes')} - {formatNumber(yesVotes, 0)} + {t('numberOfForVotes')} + {formatNumber(yesVotes, 0)} - {t('numberOfAgainstVotes')} - {formatNumber(noVotes, 0)} + {t('numberOfAgainstVotes')} + {formatNumber(noVotes, 0)} - {t('yesPercentage')} - {formatNumber(yesPercentage, 2)}% + {t('yesPercentage')} + {formatNumberPercentage(yesPercentage, 2)} - {t('noPercentage')} - {formatNumber(noPercentage, 2)}% + {t('noPercentage')} + {formatNumberPercentage(noPercentage, 2)} ); diff --git a/apps/token/src/routes/governance/components/proposals-list/proposals-list.tsx b/apps/token/src/routes/governance/components/proposals-list/proposals-list.tsx index 2d54fd418..00c9b7ad1 100644 --- a/apps/token/src/routes/governance/components/proposals-list/proposals-list.tsx +++ b/apps/token/src/routes/governance/components/proposals-list/proposals-list.tsx @@ -4,10 +4,7 @@ import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { Heading } from '../../../../components/heading'; -import { - KeyValueTable, - KeyValueTableRow, -} from '../../../../components/key-value-table'; +import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { getProposalName } from '../../../../lib/type-policies/proposal'; import type { Proposals_proposals } from '../../proposals/__generated__/Proposals'; import { CurrentProposalState } from '../current-proposal-state'; @@ -33,36 +30,34 @@ export const ProposalsList = ({ proposals }: ProposalsListProps) => { - {t('state')} - + {t('state')} + - + - - {isFuture(new Date(proposal.terms.closingDatetime)) - ? t('closesOn') - : t('closedOn')} - - + {isFuture(new Date(proposal.terms.closingDatetime)) + ? t('closesOn') + : t('closedOn')} + + {format( new Date(proposal.terms.closingDatetime), DATE_FORMAT_DETAILED )} - + - - {isFuture(new Date(proposal.terms.enactmentDatetime)) - ? t('proposedEnactment') - : t('enactedOn')} - - + {isFuture(new Date(proposal.terms.enactmentDatetime)) + ? t('proposedEnactment') + : t('enactedOn')} + + {format( new Date(proposal.terms.enactmentDatetime), DATE_FORMAT_DETAILED )} - + diff --git a/apps/token/src/routes/home/token-details/token-details-circulating.tsx b/apps/token/src/routes/home/token-details/token-details-circulating.tsx index 6b579fbda..6d856273c 100644 --- a/apps/token/src/routes/home/token-details/token-details-circulating.tsx +++ b/apps/token/src/routes/home/token-details/token-details-circulating.tsx @@ -37,8 +37,8 @@ export const TokenDetailsCirculating = ({ }) => { const totalCirculating = sumCirculatingTokens(tranches); return ( - + {formatNumber(totalCirculating, 2)} - + ); }; diff --git a/apps/token/src/routes/home/token-details/token-details.tsx b/apps/token/src/routes/home/token-details/token-details.tsx index 54e63a53d..680b5b160 100644 --- a/apps/token/src/routes/home/token-details/token-details.tsx +++ b/apps/token/src/routes/home/token-details/token-details.tsx @@ -3,10 +3,7 @@ import './token-details.scss'; import { useTranslation } from 'react-i18next'; import { EtherscanLink } from '@vegaprotocol/ui-toolkit'; -import { - KeyValueTable, - KeyValueTableRow, -} from '../../../components/key-value-table'; +import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { ADDRESSES } from '../../../config'; import { useTranches } from '../../../hooks/use-tranches'; import type { BigNumber } from '../../../lib/bignumber'; @@ -26,36 +23,34 @@ export const TokenDetails = ({ return ( - {t('Token address')} - - - + {t('Token address')} + - {t('Vesting contract')} - - - + {t('Vesting contract')} + - {t('Total supply')} - {formatNumber(totalSupply, 2)} + {t('Total supply')} + {formatNumber(totalSupply, 2)} - {t('Circulating supply')} + {t('Circulating supply')} - {t('Staked on Vega validator')} - {formatNumber(totalStaked, 2)} + {t('Staked on Vega validator')} + {formatNumber(totalStaked, 2)} ); diff --git a/apps/token/src/routes/redemption/home/vesting-table.tsx b/apps/token/src/routes/redemption/home/vesting-table.tsx index d22471060..4b8156454 100644 --- a/apps/token/src/routes/redemption/home/vesting-table.tsx +++ b/apps/token/src/routes/redemption/home/vesting-table.tsx @@ -3,10 +3,7 @@ import './vesting-table.scss'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import { - KeyValueTable, - KeyValueTableRow, -} from '../../../components/key-value-table'; +import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { BigNumber } from '../../../lib/bignumber'; import { formatNumber } from '../../../lib/format-number'; @@ -42,29 +39,29 @@ export const VestingTable = ({ data-testid="vesting-table-total" className="vesting-table__top-solid-border" > - {t('Vesting VEGA')} - {formatNumber(total)} + {t('Vesting VEGA')} + {formatNumber(total)} - -
+ + {t('Locked')} - - {formatNumber(locked)} + + {formatNumber(locked)}
- -
+ + {t('Unlocked')} - - {formatNumber(vested)} + + {formatNumber(vested)}
- -
+ + {t('Associated')} - - {formatNumber(associated)} + + {formatNumber(associated)}
diff --git a/apps/token/src/routes/redemption/tranche-table.tsx b/apps/token/src/routes/redemption/tranche-table.tsx index 7f115ebc4..487ac9518 100644 --- a/apps/token/src/routes/redemption/tranche-table.tsx +++ b/apps/token/src/routes/redemption/tranche-table.tsx @@ -4,10 +4,7 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; -import { - KeyValueTable, - KeyValueTableRow, -} from '../../components/key-value-table'; +import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { BigNumber } from '../../lib/bignumber'; import { formatNumber } from '../../lib/format-number'; import { Routes } from '../router-config'; @@ -41,16 +38,15 @@ export const Tranche0Table = ({ <> - - - {t('Tranche')} {trancheId} - - - {formatNumber(total)} + + {t('Tranche')} {trancheId} + + + {formatNumber(total)} - {t('Locked')} - {formatNumber(total)} + {t('Locked')} + {formatNumber(total)}
diff --git a/apps/token/src/routes/rewards/home/reward-info.tsx b/apps/token/src/routes/rewards/home/reward-info.tsx index ef0f2aa22..c4fb14330 100644 --- a/apps/token/src/routes/rewards/home/reward-info.tsx +++ b/apps/token/src/routes/rewards/home/reward-info.tsx @@ -5,10 +5,7 @@ import { format } from 'date-fns'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import { - KeyValueTable, - KeyValueTableRow, -} from '../../../components/key-value-table'; +import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { BigNumber } from '../../../lib/bignumber'; import { DATE_FORMAT_DETAILED } from '../../../lib/date-formats'; import type { @@ -122,26 +119,30 @@ export const RewardTable = ({ reward, delegations }: RewardTableProps) => { - {t('rewardType')} - {DEFAULT_REWARD_TYPE} + {t('rewardType')} + {DEFAULT_REWARD_TYPE} - {t('yourStake')} - {stakeForEpoch.toString()} + {t('yourStake')} + {stakeForEpoch.toString()} - {t('reward')} - + {t('reward')} + {reward.amountFormatted} {t('VEGA')} - + - {t('shareOfReward')} - {new BigNumber(reward.percentageOfTotal).dp(2).toString()}% + {t('shareOfReward')} + + {new BigNumber(reward.percentageOfTotal).dp(2).toString()}% + - {t('received')} - {format(new Date(reward.receivedAt), DATE_FORMAT_DETAILED)} + {t('received')} + + {format(new Date(reward.receivedAt), DATE_FORMAT_DETAILED)} +
diff --git a/apps/token/src/routes/staking/validator-table.tsx b/apps/token/src/routes/staking/validator-table.tsx index b3d12e4a5..65863c2b9 100644 --- a/apps/token/src/routes/staking/validator-table.tsx +++ b/apps/token/src/routes/staking/validator-table.tsx @@ -4,10 +4,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { EtherscanLink } from '@vegaprotocol/ui-toolkit'; -import { - KeyValueTable, - KeyValueTableRow, -} from '../../components/key-value-table'; +import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { BigNumber } from '../../lib/bignumber'; import { formatNumber } from '../../lib/format-number'; import type { Staking_nodes } from './__generated__/Staking'; @@ -37,59 +34,59 @@ export const ValidatorTable = ({ return ( - {t('id')}: - {node.id} + {t('id')}: + {node.id} - {t('VEGA ADDRESS / PUBLIC KEY')} - {node.pubkey} + {t('VEGA ADDRESS / PUBLIC KEY')} + {node.pubkey} - {t('ABOUT THIS VALIDATOR')} - + {t('ABOUT THIS VALIDATOR')} + {node.infoUrl} - + - {t('IP ADDRESS')} - {node.location} + {t('IP ADDRESS')} + {node.location} - {t('ETHEREUM ADDRESS')} - + {t('ETHEREUM ADDRESS')} + - + - {t('TOTAL STAKE')} - {node.stakedTotalFormatted} + {t('TOTAL STAKE')} + {node.stakedTotalFormatted} - {t('PENDING STAKE')} - {node.pendingStakeFormatted} + {t('PENDING STAKE')} + {node.pendingStakeFormatted} - {t('STAKED BY OPERATOR')} - {node.stakedByOperatorFormatted} + {t('STAKED BY OPERATOR')} + {node.stakedByOperatorFormatted} - {t('STAKED BY DELEGATES')} - {node.stakedByDelegatesFormatted} + {t('STAKED BY DELEGATES')} + {node.stakedByDelegatesFormatted} - {t('STAKE SHARE')} - {stakePercentage} + {t('STAKE SHARE')} + {stakePercentage} - {t('OWN STAKE (THIS EPOCH)')} - {formatNumber(stakeThisEpoch)} + {t('OWN STAKE (THIS EPOCH)')} + {formatNumber(stakeThisEpoch)} - {t('NOMINATED (THIS EPOCH)')} - {node.stakedByDelegatesFormatted} + {t('NOMINATED (THIS EPOCH)')} + {node.stakedByDelegatesFormatted} ); diff --git a/apps/token/src/routes/staking/your-stake.tsx b/apps/token/src/routes/staking/your-stake.tsx index f13aee4fc..1bef504c0 100644 --- a/apps/token/src/routes/staking/your-stake.tsx +++ b/apps/token/src/routes/staking/your-stake.tsx @@ -1,9 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { - KeyValueTable, - KeyValueTableRow, -} from '../../components/key-value-table'; +import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { formatNumber } from '../../lib/format-number'; import type { BigNumber } from '../../lib/bignumber'; @@ -23,12 +20,16 @@ export const YourStake = ({

{t('Your stake')}

- {t('Your Stake On Node (This Epoch)')} - {formatNumber(stakeThisEpoch)} + {t('Your Stake On Node (This Epoch)')} + + {formatNumber(stakeThisEpoch)} + - {t('Your Stake On Node (Next Epoch)')} - {formatNumber(stakeNextEpoch)} + {t('Your Stake On Node (Next Epoch)')} + + {formatNumber(stakeNextEpoch)} +
diff --git a/apps/token/src/routes/withdrawals/index.tsx b/apps/token/src/routes/withdrawals/index.tsx index f756a1d2d..f3a4e1ea8 100644 --- a/apps/token/src/routes/withdrawals/index.tsx +++ b/apps/token/src/routes/withdrawals/index.tsx @@ -10,10 +10,7 @@ import { useTranslation } from 'react-i18next'; import { EtherscanLink } from '@vegaprotocol/ui-toolkit'; import { Heading } from '../../components/heading'; -import { - KeyValueTable, - KeyValueTableRow, -} from '../../components/key-value-table'; +import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit'; import { SplashLoader } from '../../components/splash-loader'; import { TransactionButton } from '../../components/transaction-button'; import { VegaWalletContainer } from '../../components/vega-wallet-container'; @@ -197,46 +194,46 @@ export const Withdrawal = ({
- {t('Withdraw')} - + {t('Withdraw')} + {addDecimal( new BigNumber(withdrawal.amount), withdrawal.asset.decimals )}{' '} {withdrawal.asset.symbol} - + - {t('from')} - {truncateMiddle(withdrawal.party.id)} + {t('from')} + {truncateMiddle(withdrawal.party.id)} - {t('toEthereum')} - + {t('toEthereum')} + - + - {t('created')} - + {t('created')} + {format( new Date(withdrawal.createdTimestamp), DATE_FORMAT_DETAILED )} - + - {t('Signature')} - + {t('Signature')} + {!erc20Approval?.signatures ? t('Loading') : truncateMiddle(erc20Approval.signatures)} - + ( + + + {'Token address'} + {'0x888'} + + + {'Value'} + {888} + + + {'Token address'} + {'0x888'} + + +); + +export const Muted = Template.bind({}); +Muted.args = { + title: 'Muted table', + muted: true, + numerical: false, + headingLevel: 5, // h5 +}; + +export const Numerical = Template.bind({}); +Numerical.args = { + title: 'Numerical table', + muted: false, + numerical: true, + headingLevel: 5, // h5 +}; + +export const Normal = Template.bind({}); +Normal.args = { + headingLevel: 5, // h5 + title: 'Normal table', + muted: false, + numerical: false, +}; diff --git a/apps/token/src/components/key-value-table/key-value-table.test.tsx b/libs/ui-toolkit/src/components/key-value-table/key-value-table.test.tsx similarity index 55% rename from apps/token/src/components/key-value-table/key-value-table.test.tsx rename to libs/ui-toolkit/src/components/key-value-table/key-value-table.test.tsx index 7597d1f35..1add27dbd 100644 --- a/apps/token/src/components/key-value-table/key-value-table.test.tsx +++ b/libs/ui-toolkit/src/components/key-value-table/key-value-table.test.tsx @@ -1,31 +1,34 @@ import { render, screen } from '@testing-library/react'; +import type { KeyValueTableProps } from './key-value-table'; import { KeyValueTable, KeyValueTableRow } from './key-value-table'; -const props = { +const props: KeyValueTableProps = { title: 'Title', + headingLevel: 3, + children: undefined, }; it('Renders the correct elements', () => { const { container } = render( - My label - My value + My label + My value - My label 2 - My value 2 + My label 2 + My value 2 ); - expect(screen.getByText(props.title)).toBeInTheDocument(); + expect(screen.getByText(props.title || '')).toBeInTheDocument(); - expect(container.querySelector('.key-value-table')).toBeInTheDocument(); - expect(container.querySelectorAll('.key-value-table__row')).toHaveLength(2); + expect(screen.getByTestId('key-value-table')).toBeInTheDocument(); + expect(container.getElementsByTagName('dl')).toHaveLength(2); - const rows = container.querySelectorAll('.key-value-table__row'); + const rows = container.getElementsByTagName('dl'); // Row 1 expect(rows[0].firstChild).toHaveTextContent('My label'); expect(rows[0].children[1]).toHaveTextContent('My value'); @@ -37,30 +40,30 @@ it('Renders the correct elements', () => { it('Applies numeric class if prop is passed', () => { render( - + - My label - My value + My label + My value ); expect(screen.getByTestId('key-value-table')).toHaveClass( - 'key-value-table--numerical' + 'w-full border-collapse mb-8 [border-spacing:0] break-all' ); }); it('Applies muted class if prop is passed', () => { render( - + - My label - My value + My label + My value ); expect(screen.getByTestId('key-value-table')).toHaveClass( - 'key-value-table--muted' + 'w-full border-collapse mb-8 [border-spacing:0] break-all' ); }); diff --git a/libs/ui-toolkit/src/components/key-value-table/key-value-table.tsx b/libs/ui-toolkit/src/components/key-value-table/key-value-table.tsx new file mode 100644 index 000000000..d1fb13f63 --- /dev/null +++ b/libs/ui-toolkit/src/components/key-value-table/key-value-table.tsx @@ -0,0 +1,95 @@ +import classNames from 'classnames'; +import * as React from 'react'; + +export interface KeyValueTableProps + extends React.HTMLAttributes { + title?: string; + children: React.ReactNode; + headingLevel?: 1 | 2 | 3 | 4 | 5 | 6; + muted?: boolean; + numerical?: boolean; +} + +export const KeyValueTable = ({ + title, + children, + className, + muted, + numerical, + headingLevel, + ...rest +}: KeyValueTableProps) => { + const TitleTag: keyof JSX.IntrinsicElements = headingLevel + ? `h${headingLevel}` + : 'div'; + return ( + + {title && ( + + {title} + + )} +
+
+ {children && + React.Children.map( + children, + (child) => + child && + React.cloneElement( + child as React.ReactElement, + { + muted, + numerical, + } + ) + )} +
+
+
+ ); +}; + +export interface KeyValueTableRowProps + extends React.HTMLAttributes { + children: [React.ReactNode, React.ReactNode]; + className?: string; + numerical?: boolean; // makes all values monospace + muted?: boolean; +} + +export const KeyValueTableRow = ({ + children, + className, + muted, + numerical, +}: KeyValueTableRowProps) => { + const dlClassName = classNames( + 'flex flex-wrap justify-between items-center border-b first:border-t border-black dark:border-white', + { + 'border-black/60 dark:border-white/60 first:[border-top:none] last:[border-bottom:none]': + muted, + }, + className + ); + const dtClassName = `break-normal font-medium uppercase align-top p-4`; + const ddClassName = classNames( + 'align-top p-4 text-black/60 dark:text-white/60 break-normal', + { + 'font-mono': numerical, + } + ); + + return ( +
+
{children[0]}
+
{children[1]}
+
+ ); +}; diff --git a/libs/ui-toolkit/src/index.ts b/libs/ui-toolkit/src/index.ts index 41b29673e..796871d6d 100644 --- a/libs/ui-toolkit/src/index.ts +++ b/libs/ui-toolkit/src/index.ts @@ -1,27 +1,5 @@ // Components -export { AgGridLazy, AgGridDynamic } from './components/ag-grid'; -export { AsyncRenderer } from './components/async-renderer'; -export { Button, AnchorButton } from './components/button'; -export { Callout } from './components/callout'; -export { CopyWithTooltip } from './components/copy-with-tooltip'; -export { EtherscanLink } from './components/etherscan-link'; -export { FormGroup } from './components/form-group'; -export { Icon } from './components/icon'; -export { Input } from './components/input'; -export { InputError } from './components/input-error'; -export { Lozenge } from './components/lozenge'; -export { Loader } from './components/loader'; -export { Select } from './components/select'; -export { Splash } from './components/splash'; -export { TextArea } from './components/text-area'; -export { ThemeSwitcher } from './components/theme-switcher'; -export { Toggle } from './components/toggle'; -export { Dialog } from './components/dialog/dialog'; -export { VegaLogo } from './components/vega-logo'; -export { Tooltip } from './components/tooltip'; -export { Indicator } from './components/indicator'; -export { Card } from './components/card'; -export { SyntaxHighlighter } from './components/syntax-highlighter'; +export * from './components'; // Utils export * from './utils/intent';