Skip to content

feat(button): refactor antd button, rewrite icon #585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions src/button/__tests__/__snapshots__/index.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Button should support contentLayout success render 1`] = `
{
"asFragment": [Function],
"baseElement": <body>
<div>
<button
class="ant-btn ant-btn-default dtc-button"
type="button"
>
<span
class="dtc-button__icon dtc-button__icon--middle"
>
<span
data-mock-icon="UploadOutlined"
/>
</span>
<span
class="dtc-button__text dtc-button__text--middle"
>
Primary
</span>
</button>
</div>
</body>,
"container": <div>
<button
class="ant-btn ant-btn-default dtc-button"
type="button"
>
<span
class="dtc-button__icon dtc-button__icon--middle"
>
<span
data-mock-icon="UploadOutlined"
/>
</span>
<span
class="dtc-button__text dtc-button__text--middle"
>
Primary
</span>
</button>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
50 changes: 50 additions & 0 deletions src/button/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import { UploadOutlined } from '@dtinsight/react-icons';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';

import Button from '..';

describe('Button', () => {
test('should support contentLayout success render', () => {
const wrapper = render(<Button icon={<UploadOutlined />}>Primary</Button>);
expect(wrapper).toMatchSnapshot();
});

it('renders text correctly', () => {
const { getByText } = render(<Button>Hello</Button>);
expect(getByText('Hello')).toBeInTheDocument();
});

it('renders icon correctly', () => {
const { container } = render(<Button icon={<UploadOutlined />} />);
expect(container.querySelector('.dtc-button__icon')).toBeInTheDocument();
expect(container.querySelector('.dtc-button__text')).not.toBeInTheDocument();
});

it('renders icon and text correctly', () => {
const { getByText, container } = render(<Button icon={<UploadOutlined />}>Search</Button>);
expect(getByText('Search')).toBeInTheDocument();
expect(container.querySelector('.dtc-button__icon')).toBeInTheDocument();
});

it('applies custom className', () => {
const { container } = render(<Button className="custom-class">Test</Button>);
expect(container.firstChild).toHaveClass('custom-class');
});

it('passes other props to AntdButton', () => {
const { getByText } = render(<Button type="primary">Primary</Button>);
expect(getByText('Primary').parentNode).toHaveClass('ant-btn-primary');
});

it('applies size class to icon and text', () => {
const { container } = render(
<Button icon={<UploadOutlined />} size="small">
Test
</Button>
);
expect(container.querySelector('.dtc-button__icon--small')).toBeInTheDocument();
expect(container.querySelector('.dtc-button__text--small')).toBeInTheDocument();
});
});
82 changes: 82 additions & 0 deletions src/button/demos/basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { useState } from 'react';
import { UploadOutlined } from '@dtinsight/react-icons';
import { Radio, Space } from 'antd';
import { Button } from 'dt-react-component';

import { ButtonProps } from '..';

export default function Basic() {
const [size, setSize] = useState<ButtonProps['size']>('large');

return (
<Space direction="vertical" size="large">
<Radio.Group value={size} onChange={(e) => setSize(e.target.value)}>
<Radio.Button value="large">Large</Radio.Button>
<Radio.Button value="middle">Default</Radio.Button>
<Radio.Button value="small">Small</Radio.Button>
</Radio.Group>

<div>
<h3>按钮类型</h3>
<Space>
<Button type="primary" size={size}>
Primary Button
</Button>
<Button size={size}>Default Button</Button>
<Button type="dashed" size={size}>
Dashed Button
</Button>
<Button type="link" size={size}>
Link Button
</Button>
</Space>
</div>

<div>
<h3>带图标的按钮</h3>
<Space>
<Button type="primary" size={size} icon={<UploadOutlined />}>
Search
</Button>
<Button icon={<UploadOutlined />} size={size}>
Search
</Button>
<Button type="dashed" size={size} icon={<UploadOutlined />}>
Search
</Button>
<Button type="link" size={size} icon={<UploadOutlined />}>
Search
</Button>
</Space>
</div>

<div>
<h3>纯图标按钮</h3>
<Space>
<Button type="primary" size={size} icon={<UploadOutlined />} />
<Button size={size} icon={<UploadOutlined />} />
<Button size={size} type="dashed" icon={<UploadOutlined />} />
<Button size={size} type="link" icon={<UploadOutlined />} />
</Space>
</div>

<div>
<h3>禁用状态</h3>
<Space>
<Button size={size} type="primary" disabled>
Primary(Disabled)
</Button>
<Button size={size} disabled>
Default(Disabled)
</Button>
<Button size={size} type="dashed" disabled>
Dashed(Disabled)
</Button>
<Button size={size} type="link" disabled>
Link(Disabled)
</Button>
</Space>
</div>
</Space>
);
}
42 changes: 42 additions & 0 deletions src/button/demos/block.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import { UploadOutlined } from '@dtinsight/react-icons';
import { Space } from 'antd';
import { Button } from 'dt-react-component';

export default function BlockDemo() {
return (
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<div>
<h3>块级按钮</h3>
<Space direction="vertical" style={{ width: '100%' }}>
<Button type="primary" block>
Primary Block Button
</Button>
<Button block>Default Block Button</Button>
<Button type="dashed" block>
Dashed Block Button
</Button>
<Button type="link" block>
Link Block Button
</Button>
</Space>
</div>

<div style={{ background: 'rgb(190, 200, 200)', padding: '16px' }}>
<h3>幽灵按钮</h3>
<Space>
<Button type="primary" ghost>
Primary Ghost
</Button>
<Button ghost>Default Ghost</Button>
<Button type="dashed" ghost>
Dashed Ghost
</Button>
<Button type="primary" ghost icon={<UploadOutlined />}>
Ghost with Icon
</Button>
</Space>
</div>
</Space>
);
}
71 changes: 71 additions & 0 deletions src/button/demos/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { useState } from 'react';
import { UploadOutlined } from '@dtinsight/react-icons';
import { Space } from 'antd';
import { Button } from 'dt-react-component';

export default function LoadingDemo() {
const [loading, setLoading] = useState(false);

const handleClick = () => {
setLoading(true);
setTimeout(() => {
setLoading(false);
}, 2000);
};

return (
<Space direction="vertical" size="large">
<div>
<h3>加载状态</h3>
<Space>
<Button type="primary" loading>
Loading
</Button>
<Button loading>Loading</Button>
<Button type="dashed" loading>
Loading
</Button>
<Button type="link" loading>
Loading
</Button>
</Space>
</div>

<div>
<h3>点击后加载</h3>
<Space>
<Button type="primary" loading={loading} onClick={handleClick}>
Click me!
</Button>
<Button loading={loading} onClick={handleClick}>
Click me!
</Button>
<Button
type="primary"
icon={<UploadOutlined />}
loading={loading}
onClick={handleClick}
>
Click me!
</Button>
</Space>
</div>

<div>
<h3>危险按钮</h3>
<Space>
<Button type="primary" danger>
Primary Danger
</Button>
<Button danger>Default Danger</Button>
<Button type="dashed" danger>
Dashed Danger
</Button>
<Button type="link" danger>
Link Danger
</Button>
</Space>
</div>
</Space>
);
}
34 changes: 34 additions & 0 deletions src/button/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
title: Button 按钮
group: 组件
toc: content
demo:
cols: 2
---

# Button 按钮

## 何时使用

按钮用于开始一个即时操作。

## 代码演示

<code src="./demos/basic.tsx" title="基本使用" description="按钮的基本使用,包括不同类型的按钮、带图标的按钮、纯图标按钮、不同尺寸的按钮以及禁用状态的按钮。"></code>

<code src="./demos/loading.tsx" title="加载状态" description="展示按钮的加载状态和危险按钮。"></code>

<code src="./demos/block.tsx" title="块级按钮" description="展示块级按钮和幽灵按钮。"></code>

## API

### Button

Button 组件支持 antd Button 组件的所有属性,详见 [Ant Design Button API](https://ant.design/components/button-cn/#API)。

## 注意事项

- 使用自定义图标时,请确保图标组件符合规范,建议使用项目内的图标组件。
- 图标大小会根据按钮的 `size` 属性自动调整,也可以通过设置图标组件的 `style` 属性来手动调整大小。
- 当只设置 `icon` 而不设置 `children` 时,按钮将只显示图标。
- 本组件是对 antd Button 的封装,支持 antd Button 的所有属性和事件。
Loading
Loading