feat(#447): 447 - UI toolkit and theme updates
* feat: 447 Refactored 'progress' intent to be 'prompt' as now white. Added yellow 'selected' intent * feat: 447 Colour consolidation * feat: 447 Colour consolidation extra renaming * feat: 447 Fixing specified red colours * feat: 447 Removed unused darker red * feat: 447 Documenting additional colours in storybook * feat: 447 Buttons updated (except 'accent', which will probably get removed when navs built) * feat: 447 Text inputs updated * feat:frontend-monorepo-447: Trading nav * feat:frontend-monorepo-447: Updated toggle button colours * feat:frontend-monorepo-447: Custom checkboxes * feat:frontend-monorepo-447: Tweaks to radio buttons * feat:frontend-monorepo-447: Input dates get dark color scheme in dark mode * feat:frontend-monorepo-447: Dropdown updates * feat:frontend-monorepo-447: Icon menu * feat:frontend-monorepo-447: Focus visual styles moved to focus-visible for radios and toggle * feat:frontend-monorepo-447: Tweak to focus styles for text input and textarea * feat:frontend-monorepo-447: Labeled input * feat:frontend-monorepo-447: Labeled input description red when in error * feat:frontend-monorepo-447: Tooltip visual update * feat:frontend-monorepo-447: Added disabled state to checkbox * feat:frontend-monorepo-447: Custom select with radix * feat:frontend-monorepo-447: Reverted back to native Select for a11y concerns * feat:frontend-monorepo-447: Added visual cue for dropdown items when multiple can be selected * feat:frontend-monorepo-447: Removed shadow from buttons in Explorer where it looked wrong * feat:frontend-monorepo-447: Added box shadow classes into tailwind theme * feat:frontend-monorepo-447: Colour primitives documentation updated * feat:frontend-monorepo-447: Cleaning up box shadow use further * feat:frontend-monorepo-447: Intents util updated * feat:frontend-monorepo-447: Dialog component updated * feat:frontend-monorepo-447: Callout component updated * feat:frontend-monorepo-447: Adjusted apps to handle toolkit changes * feat:frontend-monorepo-447: Moved tabs to ui-toolkit and styled * feat:frontend-monorepo-447: Fixed ui-toolkit tests * feat:frontend-monorepo-447: Token eth wallet made dark to support new buttons * feat:frontend-monorepo-447: Ran prettier * frontend-monorepo-447: Simplified button class functions and exported for use on other elements * frontend-monorepo-447: Used newly exported button classes on Link elements in eth-wallet * frontend-monorepo-447: Moved trading nav from ui-toolkit to trading app * frontend-monorepo-447: Simplified intents and updated stories * frontend-monorepo-447: Using classnames in requested spot * frontend-monorepo-447: Removed unnecessary 'asChild' prop on dropdown triggers * frontend-monorepo-447: Made use of the XPrimitive Radix naming convention * frontend-monorepo-447: Simplified types in 'getButtonClasses' * frontend-monorepo-447: Added 'asChild' to dropdown trigger to avoid nested buttons * frontend-monorepo-447: Moved input label and description into Formgroup component. Refactored based on tweaked structure * frontend-monorepo-447: Externally linked input label * frontend-monorepo-447: Adding correct text colours to Intent.None backgrounds * frontend-monorepo-447: Improved intent function name * frontend-monorepo-447: Removed new navbar until implementation ticket is picked up * frontend-monorepo-447: using testing-library/user-event for tab click unit tests * frontend-monorepo-447: Removed unused button import * frontend-monorepo-447: Little extra use of classnames in form-group.tsx * feat: make navbar pink for light mode * fix: problem with theme not switching when dependent in js on theme value * fix: bg of row hover * fix: dont use vega pink for sell red * fix: type error in generate orders func * fix: lint Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
parent
4c5a026a03
commit
2302ef4378
@ -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}
|
||||
>
|
||||
|
@ -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,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -73,7 +79,7 @@ export const LockedProgress = ({
|
||||
leftLabel,
|
||||
rightLabel,
|
||||
leftColor = Colors.vega.pink,
|
||||
rightColor = Colors.green.DEFAULT,
|
||||
rightColor = Colors.vega.green,
|
||||
light = false,
|
||||
}: LockedProgressProps) => {
|
||||
const lockedPercentage = React.useMemo(() => {
|
||||
@ -85,19 +91,28 @@ 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>
|
||||
|
||||
@ -105,6 +120,6 @@ export const LockedProgress = ({
|
||||
<span>{formatNumber(locked, 2)}</span>
|
||||
<span>{formatNumber(unlocked, 2)}</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} />
|
||||
|
@ -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" />
|
||||
|
@ -25,7 +25,7 @@ export const TargetAddressMismatch = ({
|
||||
}}
|
||||
components={{
|
||||
bold: <strong />,
|
||||
red: <span className={'text-red'} />,
|
||||
red: <span className={'text-vega-red'} />,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
|
@ -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"
|
||||
|
@ -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