diff --git a/packages/web3/src/address/__tests__/__snapshots__/index.test.tsx.snap b/packages/web3/src/address/__tests__/__snapshots__/index.test.tsx.snap
new file mode 100644
index 000000000..a4b4216d5
--- /dev/null
+++ b/packages/web3/src/address/__tests__/__snapshots__/index.test.tsx.snap
@@ -0,0 +1,17 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`Address > display address with default format 1`] = `
+
+
+
+ 0x 21CD f097 4d53 a6e9 6eF0 5d7B 324a 9803 735f Fd3B
+
+
+
+`;
diff --git a/packages/web3/src/address/__tests__/index.test.tsx b/packages/web3/src/address/__tests__/index.test.tsx
new file mode 100644
index 000000000..542782419
--- /dev/null
+++ b/packages/web3/src/address/__tests__/index.test.tsx
@@ -0,0 +1,98 @@
+import { Address } from '..';
+import { fireEvent, render } from '@testing-library/react';
+import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
+import { mockClipboard } from '../../utils/test-utils';
+import { readCopyText } from '../../utils';
+
+describe('Address', () => {
+ let resetMockClipboard: () => void;
+ beforeEach(() => {
+ resetMockClipboard = mockClipboard();
+ });
+ afterEach(() => {
+ resetMockClipboard();
+ });
+
+ it('mount correctly', () => {
+ expect(() => render( )).not.toThrow();
+ });
+
+ it('display address', () => {
+ const { baseElement } = render(
+ ,
+ );
+ expect(baseElement.querySelector('.ant-web3-address')?.textContent).toBe(
+ '0x21CDf0974d53a6e96eF05d7B324a9803735fFd3B',
+ );
+ });
+
+ it('display address with ellipsis', () => {
+ const { baseElement } = render(
+ ,
+ );
+ expect(baseElement.querySelector('.ant-web3-address')?.textContent).toBe('0x21CD...Fd3B');
+ });
+
+ it('display address with ellipsis and custom clip', () => {
+ const { baseElement } = render(
+ ,
+ );
+ expect(baseElement.querySelector('.ant-web3-address')?.textContent).toBe('0x2...d3B');
+ });
+
+ it('display address with tooltip', async () => {
+ const { baseElement } = render(
+ ,
+ );
+
+ expect(baseElement.querySelector('.ant-web3-address')?.textContent).toBe('0x21CD...Fd3B');
+ fireEvent.mouseEnter(baseElement.querySelector('.ant-web3-address-text')!);
+ await vi.waitFor(() => {
+ expect(baseElement.querySelector('.ant-tooltip-inner')?.textContent).toBe(
+ '0x21CDf0974d53a6e96eF05d7B324a9803735fFd3B',
+ );
+ });
+ });
+
+ it('display address with default format', () => {
+ const { baseElement } = render(
+ ,
+ );
+ expect(baseElement.querySelector('.ant-web3-address')?.textContent).toBe(
+ '0x 21CD f097 4d53 a6e9 6eF0 5d7B 324a 9803 735f Fd3B',
+ );
+ expect(baseElement.querySelector('.ant-web3-address')).toMatchSnapshot();
+ });
+
+ it('display address with custom format', () => {
+ const { baseElement } = render(
+ {
+ return input.slice(0, 10);
+ }}
+ />,
+ );
+ expect(baseElement.querySelector('.ant-web3-address')?.textContent).toBe('0x21CDf097');
+ });
+ it('display address with copyable', async () => {
+ const { baseElement } = render(
+ ,
+ );
+ expect(baseElement.querySelector('.ant-web3-address')?.textContent).toBe('0x21CD...Fd3B');
+ fireEvent.click(baseElement.querySelector('.anticon-copy')!);
+ await vi.waitFor(() => {
+ expect(baseElement.querySelector('.ant-message')).not.toBeNull();
+ expect(baseElement.querySelector('.ant-message-notice-content')?.textContent?.trim()).toBe(
+ 'Address Copied!',
+ );
+ expect(readCopyText()).resolves.toBe('0x21CDf0974d53a6e96eF05d7B324a9803735fFd3B');
+ });
+ });
+});
diff --git a/packages/web3/src/address/demos/format.tsx b/packages/web3/src/address/demos/format.tsx
new file mode 100644
index 000000000..bf1923d93
--- /dev/null
+++ b/packages/web3/src/address/demos/format.tsx
@@ -0,0 +1,24 @@
+import { Address } from '@ant-design/web3';
+import { Space } from 'antd';
+import { formatAddress } from '../../utils';
+
+const App: React.FC = () => {
+ return (
+
+
+
+ Custom format:{' '}
+
{
+ return formatAddress(input, 5);
+ }}
+ />
+
+
+ );
+};
+
+export default App;
diff --git a/packages/web3/src/address/index.md b/packages/web3/src/address/index.md
index 0043b7d58..3a182e14e 100644
--- a/packages/web3/src/address/index.md
+++ b/packages/web3/src/address/index.md
@@ -15,6 +15,10 @@ group:
+## Format
+
+
+
## API
| Property | Description | Type | Default | Version |
@@ -23,3 +27,4 @@ group:
| copyable | Address copyable | `boolean` | `false` | - |
| address | Address | `string` | - | - |
| tooltip | Show tooltip when hover address | `boolean \|`[Tooltip.title](https://ant.design/components/tooltip-cn#api) | Displays the current full address | - |
+| format | Address format | `boolean \| (input: string) => ReactNode` | `false` | - |
diff --git a/packages/web3/src/address/index.tsx b/packages/web3/src/address/index.tsx
index 133d2da76..c7d40db94 100644
--- a/packages/web3/src/address/index.tsx
+++ b/packages/web3/src/address/index.tsx
@@ -1,10 +1,10 @@
import { CopyOutlined } from '@ant-design/icons';
import type { TooltipProps } from 'antd';
import { Space, Tooltip, message, ConfigProvider } from 'antd';
-import React, { useContext } from 'react';
+import React, { ReactNode, useContext, useMemo } from 'react';
import { useStyle } from './style';
import classNames from 'classnames';
-import { writeCopyText, fillWith0x } from '../utils';
+import { writeCopyText, fillWith0x, formatAddress } from '../utils';
export interface AddressProps {
ellipsis?:
@@ -16,14 +16,25 @@ export interface AddressProps {
address?: string;
copyable?: boolean;
tooltip?: boolean | TooltipProps['title'];
+ format?: boolean | ((address: string) => ReactNode);
}
export const Address: React.FC = (props) => {
- const { ellipsis, address, copyable, tooltip } = props;
+ const { ellipsis, address, copyable, tooltip, format = false } = props;
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('web3-address');
const { wrapSSR, hashId } = useStyle(prefixCls);
+ const mergedFormat = useMemo(() => {
+ if (typeof format === 'function') {
+ return format;
+ }
+ if (format) {
+ return formatAddress;
+ }
+ return (input: string) => input;
+ }, [format]);
+
const isEllipsis = !!ellipsis;
const { headClip = 6, tailClip = 4 } =
typeof ellipsis !== 'object'
@@ -38,14 +49,18 @@ export const Address: React.FC = (props) => {
}
const filledAddress = fillWith0x(address);
+
+ const formattedAddress = mergedFormat(filledAddress);
const displayTooltip = tooltip === undefined || tooltip === true ? filledAddress : tooltip;
return wrapSSR(
- {isEllipsis
- ? `${filledAddress.slice(0, headClip)}...${filledAddress.slice(-tailClip)}`
- : filledAddress}
+
+ {isEllipsis
+ ? `${filledAddress.slice(0, headClip)}...${filledAddress.slice(-tailClip)}`
+ : formattedAddress}
+
{copyable && (
+## 格式化
+
+
+
## API
| 属性 | 描述 | 类型 | 默认值 | 版本 |
@@ -23,3 +27,4 @@ group:
| copyable | 是否可复制 | `boolean` | `false` | - |
| address | 地址 | `string` | - | - |
| tooltip | 鼠标移入地址时展示提示 | `boolean \|` [Tooltip.title](https://ant.design/components/tooltip-cn#api) | 展示当前完整地址 | - |
+| format | 地址格式化 | `boolean \| (input: string) => ReactNode` | `false` | - |
diff --git a/packages/web3/src/connect-button/__tests__/__snapshots__/menu.test.tsx.snap b/packages/web3/src/connect-button/__tests__/__snapshots__/menu.test.tsx.snap
index 27e151c5a..4ec844fce 100644
--- a/packages/web3/src/connect-button/__tests__/__snapshots__/menu.test.tsx.snap
+++ b/packages/web3/src/connect-button/__tests__/__snapshots__/menu.test.tsx.snap
@@ -1,130 +1,6 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`ConnectButton > Should insert menu items before default menu items when pass extraItems into actionsMenu 1`] = `
-
-
-
-
- Connect Wallet
-
-
-
-
-
-`;
-
-exports[`ConnectButton > Should insert menu items before default menu items when pass extraItems into actionsMenu 2`] = `
Should insert menu items before default menu items when
-
+
0x21CD...Fd3B
@@ -150,8 +28,8 @@ exports[`ConnectButton > Should insert menu items before default menu items when