Feat/258 move key value table into monorepo (#320)
* move key-value-table to ui-toolkit and use tailwind * add key-value-table to storybook * override border width 1px from the styles of the app, remove td and th from children * clone muted and numerical props to children elements * proposal change table remove empty lines * add Roboto mono to font-mono tailwind config * remove labels and labelfor * revert change on token-details-circulating * export the whole components directory rather than explicitly individual components * add classNames, add formatNumberPercentage, remove spans, add span in token details circulating * data-testid=governance-proposal-enactmentDate and use span instead of div * use custom spacing defined in tailwind & another README.md update for running cypress in watch mode * update divs to span within the vesting table * Update README.md Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com> * borders and text visible on both dark and light themes * add headingLevel and use dl instead of tables * update styling for dl inline * remove added grey from tailwind * ignore md files Co-authored-by: madalinaraicu <“madalina@raygroup.uk”> Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com>
This commit is contained in:
parent
3af4e354cd
commit
bd3268adf0
@ -3,3 +3,4 @@
|
||||
/dist
|
||||
/coverage
|
||||
__generated__
|
||||
*.md
|
||||
|
14
README.md
14
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 <name-of-app>: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 <my-app>-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)
|
||||
|
@ -1 +0,0 @@
|
||||
export { KeyValueTable, KeyValueTableRow } from './key-value-table';
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
import './key-value-table.scss';
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
export interface KeyValueTableProps
|
||||
extends React.HTMLAttributes<HTMLTableElement> {
|
||||
title?: string;
|
||||
numerical?: boolean; // makes all values monospace
|
||||
children: React.ReactNode;
|
||||
muted?: boolean;
|
||||
}
|
||||
|
||||
export const KeyValueTable = ({
|
||||
title,
|
||||
numerical,
|
||||
children,
|
||||
muted,
|
||||
className,
|
||||
...rest
|
||||
}: KeyValueTableProps) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{title && <h3 className="key-value-table__header">{title}</h3>}
|
||||
<table
|
||||
data-testid="key-value-table"
|
||||
{...rest}
|
||||
className={`key-value-table ${className ? className : ''} ${
|
||||
numerical ? 'key-value-table--numerical' : ''
|
||||
}
|
||||
${muted ? 'key-value-table--muted' : ''}`}
|
||||
>
|
||||
<tbody>{children}</tbody>
|
||||
</table>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export interface KeyValueTableRowProps
|
||||
extends React.HTMLAttributes<HTMLTableRowElement> {
|
||||
children: [React.ReactNode, React.ReactNode];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const KeyValueTableRow = ({
|
||||
children,
|
||||
className,
|
||||
...rest
|
||||
}: KeyValueTableRowProps) => {
|
||||
return (
|
||||
<tr
|
||||
{...rest}
|
||||
className={`key-value-table__row ${className ? className : ''}`}
|
||||
>
|
||||
{children[0]}
|
||||
{children[1]}
|
||||
</tr>
|
||||
);
|
||||
};
|
@ -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)}%`;
|
||||
};
|
||||
|
@ -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 = ({
|
||||
<div>
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Connected Ethereum address')}</th>
|
||||
<td>{truncateMiddle(address)}</td>
|
||||
{t('Connected Ethereum address')}
|
||||
{truncateMiddle(address)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Amount of VEGA')}</th>
|
||||
<td>
|
||||
{t('Amount of VEGA')}
|
||||
|
||||
{state.claimData
|
||||
? formatNumber(state.claimData.claim.amount)
|
||||
: 'None'}
|
||||
</td>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Claim expires')}</th>
|
||||
<td>
|
||||
{t('Claim expires')}
|
||||
|
||||
{state.claimData?.claim.expiry
|
||||
? format(
|
||||
state.claimData?.claim.expiry * 1000,
|
||||
DATE_FORMAT_LONG
|
||||
)
|
||||
: 'No expiry'}
|
||||
</td>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Starts unlocking')}</th>
|
||||
<td>
|
||||
{t('Starts unlocking')}
|
||||
|
||||
{format(currentTranche.tranche_start, DATE_FORMAT_LONG)}
|
||||
</td>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Fully unlocked')}</th>
|
||||
<td>{format(currentTranche.tranche_end, DATE_FORMAT_LONG)}</td>
|
||||
{t('Fully unlocked')}
|
||||
{format(currentTranche.tranche_end, DATE_FORMAT_LONG)}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</div>
|
||||
|
@ -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 (
|
||||
<KeyValueTable muted={true} data-testid="proposal-change-table">
|
||||
<KeyValueTable data-testid="proposal-change-table" muted={true}>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('id')}</th>
|
||||
<td>{proposal.id}</td>
|
||||
{t('id')}
|
||||
{proposal.id}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('state')}</th>
|
||||
<td>
|
||||
{t('state')}
|
||||
<CurrentProposalState proposal={proposal} />
|
||||
</td>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>
|
||||
{isFuture(new Date(terms.closingDatetime))
|
||||
? t('closesOn')
|
||||
: t('closedOn')}
|
||||
</th>
|
||||
<td>{format(new Date(terms.closingDatetime), DATE_FORMAT_DETAILED)}</td>
|
||||
{format(new Date(terms.closingDatetime), DATE_FORMAT_DETAILED)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>
|
||||
{isFuture(new Date(terms.enactmentDatetime))
|
||||
? t('proposedEnactment')
|
||||
: t('enactedOn')}
|
||||
</th>
|
||||
<td>
|
||||
{format(new Date(terms.enactmentDatetime), DATE_FORMAT_DETAILED)}
|
||||
</td>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('proposedBy')}</th>
|
||||
<td>
|
||||
{t('proposedBy')}
|
||||
<span style={{ wordBreak: 'break-word' }}>{proposal.party.id}</span>
|
||||
</td>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('proposedOn')}</th>
|
||||
<td>{format(new Date(proposal.datetime), DATE_FORMAT_DETAILED)}</td>
|
||||
{t('proposedOn')}
|
||||
{format(new Date(proposal.datetime), DATE_FORMAT_DETAILED)}
|
||||
</KeyValueTableRow>
|
||||
{proposal.rejectionReason ? (
|
||||
<KeyValueTableRow>
|
||||
<th>{t('rejectionReason')}</th>
|
||||
<td>{proposal.rejectionReason}</td>
|
||||
{t('rejectionReason')}
|
||||
{proposal.rejectionReason}
|
||||
</KeyValueTableRow>
|
||||
) : null}
|
||||
{proposal.errorDetails ? (
|
||||
<KeyValueTableRow>
|
||||
<th>{t('errorDetails')}</th>
|
||||
<td>{proposal.errorDetails}</td>
|
||||
{t('errorDetails')}
|
||||
{proposal.errorDetails}
|
||||
</KeyValueTableRow>
|
||||
) : null}
|
||||
<KeyValueTableRow>
|
||||
<th>{t('type')}</th>
|
||||
<td>{proposal.terms.change.__typename}</td>
|
||||
{t('type')}
|
||||
{proposal.terms.change.__typename}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
);
|
||||
|
@ -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 (
|
||||
<KeyValueTable
|
||||
title={t('voteBreakdown')}
|
||||
numerical={true}
|
||||
muted={true}
|
||||
data-testid="proposal-votes-table"
|
||||
muted={true}
|
||||
numerical={true}
|
||||
>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('willPass')}</th>
|
||||
<td>{willPass ? '👍' : '👎'}</td>
|
||||
{t('willPass')}
|
||||
{willPass ? '👍' : '👎'}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('majorityMet')}</th>
|
||||
<td>{majorityMet ? '👍' : '👎'}</td>
|
||||
{t('majorityMet')}
|
||||
{majorityMet ? '👍' : '👎'}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('participationMet')}</th>
|
||||
<td>{participationMet ? '👍' : '👎'}</td>
|
||||
{t('participationMet')}
|
||||
{participationMet ? '👍' : '👎'}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('tokenForProposal')}</th>
|
||||
<td>{formatNumber(yesTokens, 2)}</td>
|
||||
{t('tokenForProposal')}
|
||||
{formatNumber(yesTokens, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('tokensAgainstProposal')}</th>
|
||||
<td>{formatNumber(noTokens, 2)}</td>
|
||||
{t('tokensAgainstProposal')}
|
||||
{formatNumber(noTokens, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('participationRequired')}</th>
|
||||
<td>{formatNumber(requiredParticipation)}%</td>
|
||||
{t('participationRequired')}
|
||||
{formatNumberPercentage(requiredParticipation)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('majorityRequired')}</th>
|
||||
<td>{formatNumber(requiredMajorityPercentage)}%</td>
|
||||
{t('majorityRequired')}
|
||||
{formatNumberPercentage(requiredMajorityPercentage)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('numberOfVotingParties')}</th>
|
||||
<td>{formatNumber(totalVotes, 0)}</td>
|
||||
{t('numberOfVotingParties')}
|
||||
{formatNumber(totalVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('totalTokensVotes')}</th>
|
||||
<td>{formatNumber(totalTokensVoted, 2)}</td>
|
||||
{t('totalTokensVotes')}
|
||||
{formatNumber(totalTokensVoted, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('totalTokenVotedPercentage')}</th>
|
||||
<td>{formatNumber(totalTokensPercentage, 2)}%</td>
|
||||
{t('totalTokenVotedPercentage')}
|
||||
{formatNumberPercentage(totalTokensPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('numberOfForVotes')}</th>
|
||||
<td>{formatNumber(yesVotes, 0)}</td>
|
||||
{t('numberOfForVotes')}
|
||||
{formatNumber(yesVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('numberOfAgainstVotes')}</th>
|
||||
<td>{formatNumber(noVotes, 0)}</td>
|
||||
{t('numberOfAgainstVotes')}
|
||||
{formatNumber(noVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('yesPercentage')}</th>
|
||||
<td>{formatNumber(yesPercentage, 2)}%</td>
|
||||
{t('yesPercentage')}
|
||||
{formatNumberPercentage(yesPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('noPercentage')}</th>
|
||||
<td>{formatNumber(noPercentage, 2)}%</td>
|
||||
{t('noPercentage')}
|
||||
{formatNumberPercentage(noPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
);
|
||||
|
@ -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) => {
|
||||
</Link>
|
||||
<KeyValueTable muted={true}>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('state')}</th>
|
||||
<td data-testid="governance-proposal-state">
|
||||
{t('state')}
|
||||
<span data-testid="governance-proposal-state">
|
||||
<CurrentProposalState proposal={proposal} />
|
||||
</td>
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>
|
||||
{isFuture(new Date(proposal.terms.closingDatetime))
|
||||
? t('closesOn')
|
||||
: t('closedOn')}
|
||||
</th>
|
||||
<td data-testid="governance-proposal-closingDate">
|
||||
|
||||
<span data-testid="governance-proposal-closingDate">
|
||||
{format(
|
||||
new Date(proposal.terms.closingDatetime),
|
||||
DATE_FORMAT_DETAILED
|
||||
)}
|
||||
</td>
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>
|
||||
{isFuture(new Date(proposal.terms.enactmentDatetime))
|
||||
? t('proposedEnactment')
|
||||
: t('enactedOn')}
|
||||
</th>
|
||||
<td data-testid="governance-proposal-enactmentDate">
|
||||
|
||||
<span data-testid="governance-proposal-enactmentDate">
|
||||
{format(
|
||||
new Date(proposal.terms.enactmentDatetime),
|
||||
DATE_FORMAT_DETAILED
|
||||
)}
|
||||
</td>
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</li>
|
||||
|
@ -37,8 +37,8 @@ export const TokenDetailsCirculating = ({
|
||||
}) => {
|
||||
const totalCirculating = sumCirculatingTokens(tranches);
|
||||
return (
|
||||
<td data-testid="circulating-supply">
|
||||
<span data-testid="circulating-supply">
|
||||
{formatNumber(totalCirculating, 2)}
|
||||
</td>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
@ -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 (
|
||||
<KeyValueTable className={'token-details'}>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Token address')}</th>
|
||||
<td data-testid="token-address">
|
||||
{t('Token address')}
|
||||
<EtherscanLink
|
||||
data-testid="token-address"
|
||||
address={ADDRESSES.vegaTokenAddress}
|
||||
text={ADDRESSES.vegaTokenAddress}
|
||||
className="font-mono"
|
||||
/>
|
||||
</td>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Vesting contract')}</th>
|
||||
<td data-testid="token-contract">
|
||||
{t('Vesting contract')}
|
||||
<EtherscanLink
|
||||
data-testid="token-contract"
|
||||
address={ADDRESSES.vestingAddress}
|
||||
text={ADDRESSES.vestingAddress}
|
||||
className="font-mono"
|
||||
/>
|
||||
</td>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Total supply')}</th>
|
||||
<td data-testid="total-supply">{formatNumber(totalSupply, 2)}</td>
|
||||
{t('Total supply')}
|
||||
<span data-testid="total-supply">{formatNumber(totalSupply, 2)}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Circulating supply')}</th>
|
||||
{t('Circulating supply')}
|
||||
<TokenDetailsCirculating tranches={tranches} />
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Staked on Vega validator')}</th>
|
||||
<td data-testid="staked">{formatNumber(totalStaked, 2)}</td>
|
||||
{t('Staked on Vega validator')}
|
||||
<span data-testid="staked">{formatNumber(totalStaked, 2)}</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
);
|
||||
|
@ -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"
|
||||
>
|
||||
<th>{t('Vesting VEGA')}</th>
|
||||
<td>{formatNumber(total)}</td>
|
||||
<span>{t('Vesting VEGA')}</span>
|
||||
{formatNumber(total)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow data-testid="vesting-table-locked">
|
||||
<th>
|
||||
<div className="vesting-table__indicator-square vesting-table__indicator-square--locked"></div>
|
||||
<span>
|
||||
<span className="vesting-table__indicator-square vesting-table__indicator-square--locked"></span>
|
||||
{t('Locked')}
|
||||
</th>
|
||||
<td>{formatNumber(locked)}</td>
|
||||
</span>
|
||||
{formatNumber(locked)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow data-testid="vesting-table-unlocked">
|
||||
<th>
|
||||
<div className="vesting-table__indicator-square vesting-table__indicator-square--unlocked"></div>
|
||||
<span>
|
||||
<span className="vesting-table__indicator-square vesting-table__indicator-square--unlocked"></span>
|
||||
{t('Unlocked')}
|
||||
</th>
|
||||
<td>{formatNumber(vested)}</td>
|
||||
</span>
|
||||
{formatNumber(vested)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow data-testid="vesting-table-staked">
|
||||
<th>
|
||||
<div className="vesting-table__indicator-square vesting-table__indicator-square--staked"></div>
|
||||
<span>
|
||||
<span className="vesting-table__indicator-square vesting-table__indicator-square--staked"></span>
|
||||
{t('Associated')}
|
||||
</th>
|
||||
<td>{formatNumber(associated)}</td>
|
||||
</span>
|
||||
{formatNumber(associated)}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
<div className="vesting-table__progress-bar">
|
||||
|
@ -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 = ({
|
||||
<>
|
||||
<KeyValueTable numerical={true}>
|
||||
<KeyValueTableRow data-testid="tranche-table-total">
|
||||
<th>
|
||||
<span className="tranche-table__label">
|
||||
{t('Tranche')} {trancheId}
|
||||
</span>
|
||||
</th>
|
||||
<td>{formatNumber(total)}</td>
|
||||
|
||||
<span>{formatNumber(total)}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow data-testid="tranche-table-locked">
|
||||
<th>{t('Locked')}</th>
|
||||
<td>{formatNumber(total)}</td>
|
||||
{t('Locked')}
|
||||
<span>{formatNumber(total)}</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
<div className="tranche-table__footer" data-testid="tranche-table-footer">
|
||||
|
@ -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) => {
|
||||
</h3>
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('rewardType')}</th>
|
||||
<td>{DEFAULT_REWARD_TYPE}</td>
|
||||
{t('rewardType')}
|
||||
<span>{DEFAULT_REWARD_TYPE}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('yourStake')}</th>
|
||||
<td>{stakeForEpoch.toString()}</td>
|
||||
{t('yourStake')}
|
||||
<span>{stakeForEpoch.toString()}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('reward')}</th>
|
||||
<td>
|
||||
{t('reward')}
|
||||
<span>
|
||||
{reward.amountFormatted} {t('VEGA')}
|
||||
</td>
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('shareOfReward')}</th>
|
||||
<td>{new BigNumber(reward.percentageOfTotal).dp(2).toString()}%</td>
|
||||
{t('shareOfReward')}
|
||||
<span>
|
||||
{new BigNumber(reward.percentageOfTotal).dp(2).toString()}%
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('received')}</th>
|
||||
<td>{format(new Date(reward.receivedAt), DATE_FORMAT_DETAILED)}</td>
|
||||
{t('received')}
|
||||
<span>
|
||||
{format(new Date(reward.receivedAt), DATE_FORMAT_DETAILED)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</div>
|
||||
|
@ -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 (
|
||||
<KeyValueTable data-testid="validator-table">
|
||||
<KeyValueTableRow>
|
||||
<th>{t('id')}:</th>
|
||||
<td className="validator-table__cell">{node.id}</td>
|
||||
<span>{t('id')}:</span>
|
||||
<span className="validator-table__cell">{node.id}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('VEGA ADDRESS / PUBLIC KEY')}</th>
|
||||
<td className="validator-table__cell">{node.pubkey}</td>
|
||||
<span>{t('VEGA ADDRESS / PUBLIC KEY')}</span>
|
||||
<span className="validator-table__cell">{node.pubkey}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('ABOUT THIS VALIDATOR')}</th>
|
||||
<td>
|
||||
<span>{t('ABOUT THIS VALIDATOR')}</span>
|
||||
<span>
|
||||
<a href={node.infoUrl}>{node.infoUrl}</a>
|
||||
</td>
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('IP ADDRESS')}</th>
|
||||
<td>{node.location}</td>
|
||||
<span>{t('IP ADDRESS')}</span>
|
||||
<span>{node.location}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('ETHEREUM ADDRESS')}</th>
|
||||
<td>
|
||||
<span>{t('ETHEREUM ADDRESS')}</span>
|
||||
<span>
|
||||
<EtherscanLink
|
||||
text={node.ethereumAdddress}
|
||||
address={node.ethereumAdddress}
|
||||
/>
|
||||
</td>
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('TOTAL STAKE')}</th>
|
||||
<td>{node.stakedTotalFormatted}</td>
|
||||
<span>{t('TOTAL STAKE')}</span>
|
||||
<span>{node.stakedTotalFormatted}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('PENDING STAKE')}</th>
|
||||
<td>{node.pendingStakeFormatted}</td>
|
||||
<span>{t('PENDING STAKE')}</span>
|
||||
<span>{node.pendingStakeFormatted}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('STAKED BY OPERATOR')}</th>
|
||||
<td>{node.stakedByOperatorFormatted}</td>
|
||||
<span>{t('STAKED BY OPERATOR')}</span>
|
||||
<span>{node.stakedByOperatorFormatted}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('STAKED BY DELEGATES')}</th>
|
||||
<td>{node.stakedByDelegatesFormatted}</td>
|
||||
<span>{t('STAKED BY DELEGATES')}</span>
|
||||
<span>{node.stakedByDelegatesFormatted}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('STAKE SHARE')}</th>
|
||||
<td>{stakePercentage}</td>
|
||||
<span>{t('STAKE SHARE')}</span>
|
||||
<span>{stakePercentage}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('OWN STAKE (THIS EPOCH)')}</th>
|
||||
<td>{formatNumber(stakeThisEpoch)}</td>
|
||||
<span>{t('OWN STAKE (THIS EPOCH)')}</span>
|
||||
<span>{formatNumber(stakeThisEpoch)}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('NOMINATED (THIS EPOCH)')}</th>
|
||||
<td>{node.stakedByDelegatesFormatted}</td>
|
||||
<span>{t('NOMINATED (THIS EPOCH)')}</span>
|
||||
<span>{node.stakedByDelegatesFormatted}</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
);
|
||||
|
@ -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 = ({
|
||||
<h2>{t('Your stake')}</h2>
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Your Stake On Node (This Epoch)')}</th>
|
||||
<td data-testid="stake-this-epoch">{formatNumber(stakeThisEpoch)}</td>
|
||||
{t('Your Stake On Node (This Epoch)')}
|
||||
<span data-testid="stake-this-epoch">
|
||||
{formatNumber(stakeThisEpoch)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Your Stake On Node (Next Epoch)')}</th>
|
||||
<td data-testid="stake-next-epoch">{formatNumber(stakeNextEpoch)}</td>
|
||||
{t('Your Stake On Node (Next Epoch)')}
|
||||
<span data-testid="stake-next-epoch">
|
||||
{formatNumber(stakeNextEpoch)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</div>
|
||||
|
@ -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 = ({
|
||||
<div>
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Withdraw')}</th>
|
||||
<td>
|
||||
{t('Withdraw')}
|
||||
<span>
|
||||
{addDecimal(
|
||||
new BigNumber(withdrawal.amount),
|
||||
withdrawal.asset.decimals
|
||||
)}{' '}
|
||||
{withdrawal.asset.symbol}
|
||||
</td>
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('from')}</th>
|
||||
<td>{truncateMiddle(withdrawal.party.id)}</td>
|
||||
{t('from')}
|
||||
<span>{truncateMiddle(withdrawal.party.id)}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('toEthereum')}</th>
|
||||
<td>
|
||||
{t('toEthereum')}
|
||||
<span>
|
||||
<EtherscanLink
|
||||
address={withdrawal.details?.receiverAddress as string}
|
||||
text={truncateMiddle(
|
||||
withdrawal.details?.receiverAddress as string
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('created')}</th>
|
||||
<td>
|
||||
{t('created')}
|
||||
<span>
|
||||
{format(
|
||||
new Date(withdrawal.createdTimestamp),
|
||||
DATE_FORMAT_DETAILED
|
||||
)}
|
||||
</td>
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<th>{t('Signature')}</th>
|
||||
<td title={erc20Approval?.signatures}>
|
||||
{t('Signature')}
|
||||
<span title={erc20Approval?.signatures}>
|
||||
{!erc20Approval?.signatures
|
||||
? t('Loading')
|
||||
: truncateMiddle(erc20Approval.signatures)}
|
||||
</td>
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
<TransactionButton
|
||||
|
@ -107,7 +107,7 @@ module.exports = {
|
||||
full: '9999px',
|
||||
},
|
||||
fontFamily: {
|
||||
mono: defaultTheme.fontFamily.mono,
|
||||
mono: ['Roboto Mono', ...defaultTheme.fontFamily.mono],
|
||||
serif: defaultTheme.fontFamily.serif,
|
||||
sans: [
|
||||
'"Helvetica Neue"',
|
||||
|
24
libs/ui-toolkit/src/components/index.ts
Normal file
24
libs/ui-toolkit/src/components/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
export * from './ag-grid';
|
||||
export * from './async-renderer';
|
||||
export * from './button';
|
||||
export * from './callout';
|
||||
export * from './card';
|
||||
export * from './copy-with-tooltip';
|
||||
export * from './dialog';
|
||||
export * from './etherscan-link';
|
||||
export * from './form-group';
|
||||
export * from './icon';
|
||||
export * from './indicator';
|
||||
export * from './input';
|
||||
export * from './input-error';
|
||||
export * from './key-value-table';
|
||||
export * from './loader';
|
||||
export * from './lozenge';
|
||||
export * from './select';
|
||||
export * from './splash';
|
||||
export * from './text-area';
|
||||
export * from './theme-switcher';
|
||||
export * from './toggle';
|
||||
export * from './tooltip';
|
||||
export * from './vega-logo';
|
||||
export * from './syntax-highlighter';
|
1
libs/ui-toolkit/src/components/key-value-table/index.ts
Normal file
1
libs/ui-toolkit/src/components/key-value-table/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './key-value-table';
|
@ -0,0 +1,48 @@
|
||||
import type { Story, Meta } from '@storybook/react';
|
||||
import { KeyValueTable, KeyValueTableRow } from './key-value-table';
|
||||
|
||||
export default {
|
||||
component: KeyValueTable,
|
||||
title: 'KeyValueTable',
|
||||
} as Meta;
|
||||
|
||||
const Template: Story = (args) => (
|
||||
<KeyValueTable {...args}>
|
||||
<KeyValueTableRow>
|
||||
{'Token address'}
|
||||
{'0x888'}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{'Value'}
|
||||
{888}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{'Token address'}
|
||||
{'0x888'}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
);
|
||||
|
||||
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,
|
||||
};
|
@ -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(
|
||||
<KeyValueTable {...props}>
|
||||
<KeyValueTableRow>
|
||||
<td>My label</td>
|
||||
<td>My value</td>
|
||||
<span>My label</span>
|
||||
<span>My value</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<td>My label 2</td>
|
||||
<td>My value 2</td>
|
||||
<span>My label 2</span>
|
||||
<span>My value 2</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
);
|
||||
|
||||
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(
|
||||
<KeyValueTable numerical={true} {...props}>
|
||||
<KeyValueTable {...props} numerical={true}>
|
||||
<KeyValueTableRow>
|
||||
<td>My label</td>
|
||||
<td>My value</td>
|
||||
<span>My label</span>
|
||||
<span>My value</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
);
|
||||
|
||||
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(
|
||||
<KeyValueTable muted={true} {...props}>
|
||||
<KeyValueTable {...props} muted={true}>
|
||||
<KeyValueTableRow>
|
||||
<td>My label</td>
|
||||
<td>My value</td>
|
||||
<span>My label</span>
|
||||
<span>My value</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('key-value-table')).toHaveClass(
|
||||
'key-value-table--muted'
|
||||
'w-full border-collapse mb-8 [border-spacing:0] break-all'
|
||||
);
|
||||
});
|
@ -0,0 +1,95 @@
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface KeyValueTableProps
|
||||
extends React.HTMLAttributes<HTMLTableElement> {
|
||||
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 (
|
||||
<React.Fragment>
|
||||
{title && (
|
||||
<TitleTag className={`text-h${headingLevel} mt-8 mb-4`}>
|
||||
{title}
|
||||
</TitleTag>
|
||||
)}
|
||||
<div
|
||||
data-testid="key-value-table"
|
||||
{...rest}
|
||||
className={`w-full border-collapse mb-8 [border-spacing:0] break-all ${
|
||||
className ? className : ''
|
||||
}`}
|
||||
>
|
||||
<div>
|
||||
{children &&
|
||||
React.Children.map(
|
||||
children,
|
||||
(child) =>
|
||||
child &&
|
||||
React.cloneElement(
|
||||
child as React.ReactElement<KeyValueTableRowProps>,
|
||||
{
|
||||
muted,
|
||||
numerical,
|
||||
}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export interface KeyValueTableRowProps
|
||||
extends React.HTMLAttributes<HTMLTableRowElement> {
|
||||
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 (
|
||||
<dl className={dlClassName}>
|
||||
<dt className={dtClassName}>{children[0]}</dt>
|
||||
<dd className={ddClassName}>{children[1]}</dd>
|
||||
</dl>
|
||||
);
|
||||
};
|
@ -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';
|
||||
|
Loading…
Reference in New Issue
Block a user