Commit 8b1cf660 authored by tom's avatar tom

tests

parent b26c40e4
/* eslint-disable max-len */
import type { SmartContract } from 'types/api/contract';
export const verified: Partial<SmartContract> = {
abi: [ { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'src', type: 'address' }, { indexed: true, internalType: 'address', name: 'guy', type: 'address' }, { indexed: false, internalType: 'uint256', name: 'wad', type: 'uint256' } ], name: 'Approval', type: 'event' } ],
can_be_visualized_via_sol2uml: true,
compiler_version: 'v0.5.16+commit.9c3226ce',
constructor_args: 'constructor_args',
creation_bytecode: 'creation_bytecode',
deployed_bytecode: 'deployed_bytecode',
compiler_settings: 'compiler_settings',
evm_version: 'default',
is_verified: true,
name: 'WPOA',
optimization_enabled: true,
optimization_runs: 1500,
source_code: 'source_code',
verified_at: '2021-08-03T10:40:41.679421Z',
decoded_constructor_args: [
[ '0xc59615da2da226613b1c78f0c6676cac497910bc', { internalType: 'address', name: '_token', type: 'address' } ],
[ '1800', { internalType: 'uint256', name: '_duration', type: 'uint256' } ],
[ '900000000', { internalType: 'uint256', name: '_totalSupply', type: 'uint256' } ],
],
external_libraries: [
{ address_hash: '0xa62744BeE8646e237441CDbfdedD3458861748A8', name: 'Sol' },
{ address_hash: '0xa62744BeE8646e237441CDbfdedD3458861748A8', name: 'math' },
],
};
export const withMultiplePaths: Partial<SmartContract> = {
...verified,
file_path: './simple_storage.sol',
additional_sources: [
{
file_path: 'contracts/1_Storage.sol',
source_code: '// SPDX-License-Identifier: GPL-3.0 \n pragma solidity >=0.7.0 <0.9.0; \n contract Storage {\n //2112313123; \nuint256 number; \n function store(uint256 num) public {\nnumber = num;\n}\n function retrieve() public view returns (uint256)\n {\nreturn number;\n}\n}',
},
],
};
export const verifiedViaSourcify: Partial<SmartContract> = {
...verified,
is_verified_via_sourcify: true,
is_fully_verified: false,
is_partially_verified: true,
sourcify_repo_url: 'https://repo.sourcify.dev/contracts//full_match/99/0x51891596E158b2857e5356DC847e2D15dFbCF2d0/',
};
export const withTwinAddress: Partial<SmartContract> = {
...verified,
is_verified: false,
verified_twin_address_hash: '0xa62744bee8646e237441cdbfdedd3458861748a8',
};
export const selfDestructed: Partial<SmartContract> = {
...verified,
is_self_destructed: true,
};
export const withChangedByteCode: Partial<SmartContract> = {
...verified,
is_changed_bytecode: true,
};
export const nonVerified: Partial<SmartContract> = {
is_verified: false,
creation_bytecode: 'creation_bytecode',
deployed_bytecode: 'deployed_bytecode',
};
import type {
SmartContractQueryMethodReadError,
SmartContractQueryMethodReadSuccess,
SmartContractReadMethod,
SmartContractWriteMethod,
} from 'types/api/contract';
export const read: Array<SmartContractReadMethod> = [
{
constant: true,
inputs: [
{ internalType: 'address', name: '', type: 'address' },
],
method_id: '70a08231',
name: 'balanceOf',
outputs: [
{ internalType: 'uint256', name: '', type: 'uint256', value: '' },
],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: true,
inputs: [],
method_id: '06fdde03',
name: 'name',
outputs: [
{ internalType: 'string', name: '', type: 'string', value: 'Wrapped POA' },
],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: true,
inputs: [],
method_id: '18160ddd',
name: 'totalSupply',
outputs: [
{ internalType: 'uint256', name: '', type: 'uint256', value: '139905710421584994690047413' },
],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: true,
error: '(-32015) VM execution error. (revert)',
inputs: [],
method_id: 'df0ad3de',
name: 'upgradeabilityAdmin',
outputs: [
{ name: '', type: 'address', value: '' },
],
payable: false,
stateMutability: 'view',
type: 'function',
},
];
export const readResultSuccess: SmartContractQueryMethodReadSuccess = {
is_error: false,
result: {
names: [ 'uint256' ],
output: [
{ type: 'uint256', value: '42' },
],
},
};
export const readResultError: SmartContractQueryMethodReadError = {
is_error: true,
result: {
message: 'Some shit happened',
code: -32017,
},
};
export const write: Array<SmartContractWriteMethod> = [
{
payable: true,
stateMutability: 'payable',
type: 'fallback',
},
{
constant: false,
inputs: [
{ internalType: 'address', name: 'guy', type: 'address' },
{ internalType: 'uint256', name: 'wad', type: 'uint256' },
],
name: 'approve',
outputs: [
{ internalType: 'bool', name: '', type: 'bool' },
],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
constant: false,
inputs: [
{ internalType: 'address', name: 'src', type: 'address' },
{ internalType: 'address', name: 'dst', type: 'address' },
],
name: 'transferFrom',
outputs: [
{ internalType: 'bool', name: '', type: 'bool' },
],
payable: true,
stateMutability: 'payable',
type: 'function',
},
{
stateMutability: 'payable',
type: 'receive',
},
{
constant: false,
inputs: [],
name: 'pause',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
constant: false,
inputs: [
{ name: '_from', type: 'address' },
{ name: '_to', type: 'address' },
{ name: '_tokenId', type: 'uint256' },
{ name: '_data', type: 'bytes' },
],
name: 'safeTransferFrom',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
constant: false,
inputs: [
{ name: '_tokenId', type: 'uint256' },
{ name: '_hash', type: 'bytes32' },
{ name: '_keepRequestToken', type: 'bool' },
{ name: '_newOwner', type: 'address' },
{ name: '_signature', type: 'bytes' },
],
name: 'requestToken',
outputs: [
{ name: 'reward', type: 'uint256' },
],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
constant: false,
inputs: [
{ name: '_tokenId', type: 'uint256' },
{ name: '_imprint', type: 'bytes32' },
{ name: '_uri', type: 'string' },
{ name: '_initialKey', type: 'address' },
{ name: '_tokenRecoveryTimestamp', type: 'uint256' },
{ name: '_initialKeyIsRequestKey', type: 'bool' },
],
name: 'hydrateToken',
outputs: [
{ name: '', type: 'uint256' },
],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
];
import type { PlaywrightTestConfig } from '@playwright/experimental-ct-react';
import { devices } from '@playwright/experimental-ct-react';
import react from '@vitejs/plugin-react';
import dynamicImport from 'vite-plugin-dynamic-import';
import svgr from 'vite-plugin-svgr';
import tsconfigPaths from 'vite-tsconfig-paths';
......@@ -50,6 +51,7 @@ const config: PlaywrightTestConfig = {
svgr({
exportAsDefault: true,
}),
dynamicImport(),
],
},
},
......
import { ChakraProvider } from '@chakra-ui/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { providers } from 'ethers';
import React from 'react';
import { createClient, WagmiConfig } from 'wagmi';
import { mainnet } from 'wagmi/chains';
import { MockConnector } from 'wagmi/connectors/mock';
import { AppContextProvider } from 'lib/appContext';
import type { Props as PageProps } from 'lib/next/getServerSideProps';
......@@ -8,6 +12,27 @@ import { SocketProvider } from 'lib/socket/context';
import { PORT } from 'playwright/fixtures/socketServer';
import theme from 'theme';
const provider = new providers.JsonRpcProvider(
'http://localhost:8545',
{
name: 'POA',
chainId: 99,
},
);
const connector = new MockConnector({
chains: [ mainnet ],
options: {
signer: provider.getSigner(),
},
});
const wagmiClient = createClient({
autoConnect: true,
connectors: [ connector ],
provider,
});
type Props = {
children: React.ReactNode;
withSocket?: boolean;
......@@ -38,7 +63,9 @@ const TestApp = ({ children, withSocket, appContext = defaultAppContext }: Props
<QueryClientProvider client={ queryClient }>
<SocketProvider url={ withSocket ? `ws://localhost:${ PORT }` : undefined }>
<AppContextProvider { ...appContext }>
{ children }
<WagmiConfig client={ wagmiClient }>
{ children }
</WagmiConfig>
</AppContextProvider>
</SocketProvider>
</QueryClientProvider>
......
import type { Abi } from 'abitype';
export type SmartContractMethodArgType = 'address' | 'uint256' | 'bool' | 'string' | 'bytes' | 'bytes32';
export type SmartContractMethodStateMutability = 'view' | 'nonpayable' | 'payable';
export interface SmartContract {
deployed_bytecode: string | null;
creation_bytecode: string | null;
......@@ -34,9 +37,9 @@ export interface SmartContract {
export type SmartContractDecodedConstructorArg = [
string,
{
internalType: string;
internalType: SmartContractMethodArgType;
name: string;
type: string;
type: SmartContractMethodArgType;
}
]
......@@ -50,7 +53,7 @@ export interface SmartContractMethodBase {
outputs: Array<SmartContractMethodOutput>;
constant: boolean;
name: string;
stateMutability: 'view' | 'nonpayable' | 'payable';
stateMutability: SmartContractMethodStateMutability;
type: 'function';
payable: boolean;
error?: string;
......@@ -61,11 +64,13 @@ export interface SmartContractReadMethod extends SmartContractMethodBase {
}
export interface SmartContractWriteFallback {
payable?: true;
stateMutability: 'payable';
type: 'fallback';
}
export interface SmartContractWriteReceive {
payable?: true;
stateMutability: 'payable';
type: 'receive';
}
......@@ -75,9 +80,9 @@ export type SmartContractWriteMethod = SmartContractMethodBase | SmartContractWr
export type SmartContractMethod = SmartContractReadMethod | SmartContractWriteMethod;
export interface SmartContractMethodInput {
internalType: string;
internalType?: SmartContractMethodArgType;
name: string;
type: 'address' | 'uint256' | 'bool';
type: SmartContractMethodArgType;
}
export interface SmartContractMethodOutput extends SmartContractMethodInput {
......
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import * as contractMock from 'mocks/contract/info';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import ContractCode from './ContractCode';
const addressHash = 'hash';
const CONTRACT_API_URL = buildApiUrl('contract', { id: addressHash });
const hooksConfig = {
router: {
query: { id: addressHash },
},
};
test('verified with changed byte code +@mobile +@dark-mode', async({ mount, page }) => {
await page.route(CONTRACT_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMock.withChangedByteCode),
}));
const component = await mount(
<TestApp>
<ContractCode/>
</TestApp>,
{ hooksConfig },
);
await expect(component).toHaveScreenshot();
});
test('verified with multiple sources', async({ mount, page }) => {
await page.route(CONTRACT_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMock.withMultiplePaths),
}));
await mount(
<TestApp>
<ContractCode/>
</TestApp>,
{ hooksConfig },
);
const section = page.locator('section', { hasText: 'Contract source code' });
await expect(section).toHaveScreenshot();
});
test('verified via sourcify', async({ mount, page }) => {
await page.route(CONTRACT_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMock.verifiedViaSourcify),
}));
await mount(
<TestApp>
<ContractCode/>
</TestApp>,
{ hooksConfig },
);
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1200, height: 110 } });
});
test('self destructed', async({ mount, page }) => {
await page.route(CONTRACT_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMock.selfDestructed),
}));
await mount(
<TestApp>
<ContractCode/>
</TestApp>,
{ hooksConfig },
);
const section = page.locator('section', { hasText: 'Contract creation code' });
await expect(section).toHaveScreenshot();
});
test('with twin address alert +@mobile', async({ mount, page }) => {
await page.route(CONTRACT_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMock.withTwinAddress),
}));
const component = await mount(
<TestApp>
<ContractCode/>
</TestApp>,
{ hooksConfig },
);
await expect(component.getByRole('alert')).toHaveScreenshot();
});
test('non verified', async({ mount, page }) => {
await page.route(CONTRACT_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMock.nonVerified),
}));
const component = await mount(
<TestApp>
<ContractCode/>
</TestApp>,
{ hooksConfig },
);
await expect(component).toHaveScreenshot();
});
import { Flex, Skeleton, Button, Grid, GridItem, Text, Alert, Link, chakra, Box } from '@chakra-ui/react';
import dynamic from 'next/dynamic';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -12,10 +11,7 @@ import DataFetchAlert from 'ui/shared/DataFetchAlert';
import ExternalLink from 'ui/shared/ExternalLink';
import RawDataSnippet from 'ui/shared/RawDataSnippet';
const DynamicContractSourceCode = dynamic(
() => import('./ContractSourceCode'),
{ ssr: false },
);
import type TContractSourceCode from './ContractSourceCode';
const InfoItem = ({ label, value }: { label: string; value: string }) => (
<GridItem display="flex" columnGap={ 6 }>
......@@ -26,6 +22,11 @@ const InfoItem = ({ label, value }: { label: string; value: string }) => (
const ContractCode = () => {
const router = useRouter();
const [ ContractSourceCode, setContractSourceCode ] = React.useState<typeof TContractSourceCode | null>(null);
React.useEffect(() => {
import('./ContractSourceCode').then((component) => setContractSourceCode(component.default));
}, []);
const addressHash = router.query.id?.toString();
const { data, isLoading, isError } = useApiQuery('contract', {
......@@ -153,8 +154,8 @@ const ContractCode = () => {
textareaMaxHeight="200px"
/>
) }
{ data.source_code && (
<DynamicContractSourceCode
{ data.source_code && ContractSourceCode && (
<ContractSourceCode
data={ data.source_code }
hasSol2Yml={ Boolean(data.can_be_visualized_via_sol2uml) }
address={ addressHash }
......
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import * as contractMethodsMock from 'mocks/contract/methods';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import ContractRead from './ContractRead';
const addressHash = 'hash';
const CONTRACT_READ_METHODS_API_URL = buildApiUrl('contract_methods_read', { id: addressHash });
const CONTRACT_QUERY_METHOD_API_URL = buildApiUrl('contract_method_query', { id: addressHash });
const hooksConfig = {
router: {
query: { id: addressHash },
},
};
test('base view +@mobile +@dark-mode', async({ mount, page }) => {
await page.route(CONTRACT_READ_METHODS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMethodsMock.read),
}));
await page.route(CONTRACT_QUERY_METHOD_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMethodsMock.readResultSuccess),
}));
const component = await mount(
<TestApp>
<ContractRead/>
</TestApp>,
{ hooksConfig },
);
await component.getByText(/expand all/i).click();
await expect(component).toHaveScreenshot();
await component.getByPlaceholder(/address/i).type('address-hash');
await component.getByText(/query/i).click();
await component.getByText(/wei/i).click();
await expect(component).toHaveScreenshot();
});
test('error result', async({ mount, page }) => {
await page.route(CONTRACT_READ_METHODS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMethodsMock.read),
}));
await page.route(CONTRACT_QUERY_METHOD_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMethodsMock.readResultError),
}));
const component = await mount(
<TestApp>
<ContractRead/>
</TestApp>,
{ hooksConfig },
);
await component.getByText(/expand all/i).click();
await component.getByPlaceholder(/address/i).type('address-hash');
await component.getByText(/query/i).click();
const section = page.locator('section', { hasText: 'balanceOf' });
await expect(section).toHaveScreenshot();
});
......@@ -38,19 +38,19 @@ const ContractSourceCode = ({ data, hasSol2Yml, address, isViper, filePath, addi
if (!additionalSource?.length) {
return (
<Box>
<section>
<Flex justifyContent="space-between" alignItems="center" mb={ 3 }>
{ heading }
{ diagramLink }
<CopyToClipboard text={ data }/>
</Flex>
<CodeEditor value={ data } id="source_code"/>
</Box>
</section>
);
}
return (
<Box>
<section>
<Flex justifyContent="space-between" alignItems="center" mb={ 3 }>
{ heading }
{ diagramLink }
......@@ -66,7 +66,7 @@ const ContractSourceCode = ({ data, hasSol2Yml, address, isViper, filePath, addi
</Box>
)) }
</Flex>
</Box>
</section>
);
};
......
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import * as contractMethodsMock from 'mocks/contract/methods';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import ContractWrite from './ContractWrite';
const addressHash = 'hash';
const CONTRACT_READ_METHODS_API_URL = buildApiUrl('contract_methods_write', { id: addressHash });
const hooksConfig = {
router: {
query: { id: addressHash },
},
};
test('base view +@mobile', async({ mount, page }) => {
await page.route(CONTRACT_READ_METHODS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contractMethodsMock.write),
}));
const component = await mount(
<TestApp>
<ContractWrite/>
</TestApp>,
{ hooksConfig },
);
await component.getByText(/expand all/i).click();
await expect(component).toHaveScreenshot();
});
import { Box, chakra, Link, Spinner } from '@chakra-ui/react';
import React from 'react';
import { useWaitForTransaction } from 'wagmi';
import type { ContractMethodWriteResult } from './types';
import link from 'lib/link/link';
import ContractWriteResultDumb from './ContractWriteResultDumb';
interface Props {
result: ContractMethodWriteResult;
......@@ -17,82 +16,7 @@ const ContractWriteResult = ({ result, onSettle }: Props) => {
hash: txHash,
});
React.useEffect(() => {
if (txInfo.status !== 'loading') {
onSettle();
}
}, [ onSettle, txInfo.status ]);
// eslint-disable-next-line no-console
console.log('__>__ txInfo', txInfo);
if (!result) {
return null;
}
const isErrorResult = 'message' in result;
const txLink = (
<Link href={ link('tx', { id: txHash }) }>View transaction details</Link>
);
const content = (() => {
if (isErrorResult) {
return (
<>
<span>Error: </span>
<span>{ result.message }</span>
</>
);
}
switch (txInfo.status) {
case 'success': {
return (
<>
<span>Transaction has been confirmed. </span>
{ txLink }
</>
);
}
case 'loading': {
return (
<>
<Spinner size="sm" mr={ 3 }/>
<chakra.span verticalAlign="text-bottom">
{ 'Waiting for transaction\'s confirmation. ' }
{ txLink }
</chakra.span>
</>
);
}
case 'error': {
return (
<>
<span>Error: </span>
<span>{ txInfo.error ? txInfo.error.message : 'Something went wrong' } </span>
{ txLink }
</>
);
}
}
})();
return (
<Box
fontSize="sm"
pl={ 3 }
mt={ 3 }
alignItems="center"
whiteSpace="pre-wrap"
wordBreak="break-all"
color={ txInfo.status === 'error' || isErrorResult ? 'red.600' : undefined }
>
{ content }
</Box>
);
return <ContractWriteResultDumb result={ result } onSettle={ onSettle } txInfo={ txInfo }/>;
};
export default React.memo(ContractWriteResult);
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
import ContractWriteResultDumb from './ContractWriteResultDumb';
test('loading', async({ mount }) => {
const props = {
txInfo: {
status: 'loading' as const,
error: null,
},
result: {
hash: '0x363574E6C5C71c343d7348093D84320c76d5Dd29',
},
onSettle: () => {},
};
const component = await mount(
<TestApp>
<ContractWriteResultDumb { ...props }/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('success', async({ mount }) => {
const props = {
txInfo: {
status: 'success' as const,
error: null,
},
result: {
hash: '0x363574E6C5C71c343d7348093D84320c76d5Dd29',
},
onSettle: () => {},
};
const component = await mount(
<TestApp>
<ContractWriteResultDumb { ...props }/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('error +@mobile', async({ mount }) => {
const props = {
txInfo: {
status: 'error' as const,
error: {
// eslint-disable-next-line max-len
message: 'missing revert data in call exception; Transaction reverted without a reason string [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ]',
} as Error,
},
result: {
hash: '0x363574E6C5C71c343d7348093D84320c76d5Dd29',
},
onSettle: () => {},
};
const component = await mount(
<TestApp>
<ContractWriteResultDumb { ...props }/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('error in result', async({ mount }) => {
const props = {
txInfo: {
status: 'idle' as const,
error: null,
},
result: {
message: 'wallet is not connected',
} as Error,
onSettle: () => {},
};
const component = await mount(
<TestApp>
<ContractWriteResultDumb { ...props }/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
import { Box, chakra, Link, Spinner } from '@chakra-ui/react';
import React from 'react';
import type { ContractMethodWriteResult } from './types';
import link from 'lib/link/link';
interface Props {
result: ContractMethodWriteResult;
onSettle: () => void;
txInfo: {
status: 'loading' | 'success' | 'error' | 'idle';
error: Error | null;
};
}
const ContractWriteResultDumb = ({ result, onSettle, txInfo }: Props) => {
const txHash = result && 'hash' in result ? result.hash : undefined;
React.useEffect(() => {
if (txInfo.status !== 'loading') {
onSettle();
}
}, [ onSettle, txInfo.status ]);
if (!result) {
return null;
}
const isErrorResult = 'message' in result;
const txLink = (
<Link href={ link('tx', { id: txHash }) }>View transaction details</Link>
);
const content = (() => {
if (isErrorResult) {
return (
<>
<span>Error: </span>
<span>{ result.message }</span>
</>
);
}
switch (txInfo.status) {
case 'success': {
return (
<>
<span>Transaction has been confirmed. </span>
{ txLink }
</>
);
}
case 'loading': {
return (
<>
<Spinner size="sm" mr={ 3 }/>
<chakra.span verticalAlign="text-bottom">
{ 'Waiting for transaction\'s confirmation. ' }
{ txLink }
</chakra.span>
</>
);
}
case 'error': {
return (
<>
<span>Error: </span>
<span>{ txInfo.error ? txInfo.error.message : 'Something went wrong' } </span>
{ txLink }
</>
);
}
}
})();
return (
<Box
fontSize="sm"
pl={ 3 }
mt={ 3 }
alignItems="center"
whiteSpace="pre-wrap"
wordBreak="break-all"
color={ txInfo.status === 'error' || isErrorResult ? 'red.600' : undefined }
>
{ content }
</Box>
);
};
export default React.memo(ContractWriteResultDumb);
......@@ -16,7 +16,7 @@ const RawDataSnippet = ({ data, className, title, rightSlot, beforeSlot, textare
// see issue in theme/components/Textarea.ts
const bgColor = useColorModeValue('#f5f5f6', '#1a1b1b');
return (
<Box className={ className }>
<Box className={ className } as="section" title={ title }>
<Flex justifyContent={ title ? 'space-between' : 'flex-end' } alignItems="center" mb={ 3 }>
{ title && <Text fontWeight={ 500 }>{ title }</Text> }
{ rightSlot }
......
......@@ -7096,7 +7096,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-glob@^3.2.9:
fast-glob@^3.2.12, fast-glob@^3.2.9:
version "3.2.12"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
......@@ -11028,6 +11028,13 @@ valtio@1.8.2:
proxy-compare "2.4.0"
use-sync-external-store "1.2.0"
vite-plugin-dynamic-import@^1.2.6:
version "1.2.6"
resolved "https://registry.yarnpkg.com/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.2.6.tgz#0e4ff699d4e97cdff3ccf9e979957167bc22654b"
integrity sha512-HeO5GsQfsiGfFDLhFOUZeqjM4Y8Xk5ctfRfkw5qLszZT5+N37mMbwqp8TqYxXZ4dd0EBnQ9Zg4Pr5mR2eE9hBA==
dependencies:
fast-glob "^3.2.12"
vite-plugin-svgr@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/vite-plugin-svgr/-/vite-plugin-svgr-2.2.2.tgz#c5c9cb573bf455bb079550531847ddc5d2e122af"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment