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 ( + +
+ Default format:
+
+
+ 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`] = ` - -
- -
-
-
- - -
- -`; - -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 `; exports[`ConnectButton > Should override menu items when pass items into actionsMenu 1`] = ` - -
    - -
    -
    -
    - - -
    - -`; - -exports[`ConnectButton > Should override menu items when pass items into actionsMenu 2`] = `
      Should override menu items when pass items into actions `; exports[`ConnectButton > Should show menu when hover button 1`] = ` - -
      - -
      -
      -
      - - -
      - -`; - -exports[`ConnectButton > Should show menu when hover button 2`] = `
        match snapshot 1`] = `
        - + 0x21CDf0974d53a6e96eF05d7B324a9803735fFd3B
        diff --git a/packages/web3/src/utils/format.ts b/packages/web3/src/utils/format.ts index 78239be38..6a354bb11 100644 --- a/packages/web3/src/utils/format.ts +++ b/packages/web3/src/utils/format.ts @@ -2,3 +2,19 @@ export const fillWith0x = (address: string = ''): string => { const filledAddress = address.startsWith('0x') ? address : `0x${address}`; return filledAddress; }; + +export const formatAddress = (address: string = '', groupSize = 4): string => { + const formattedGroups = []; + + const has0x = address.startsWith('0x'); + const formatText = has0x ? address.slice(2) : address; + + for (let i = 0; i < formatText.length; i += groupSize) { + const group = formatText.slice(i, i + groupSize); + formattedGroups.push(group); + } + + const formattedText = formattedGroups.join(' '); + + return has0x ? `0x ${formattedText}` : formattedText; +};