('large');
+
+ return (
+
+ setSize(e.target.value)}>
+ Large
+ Default
+ Small
+
+
+
+
按钮类型
+
+
+
+
+
+
+
+
+
+
带图标的按钮
+
+ }>
+ Search
+
+ } size={size}>
+ Search
+
+ }>
+ Search
+
+ }>
+ Search
+
+
+
+
+
+
纯图标按钮
+
+ } />
+ } />
+ } />
+ } />
+
+
+
+
+
禁用状态
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/button/demos/block.tsx b/src/button/demos/block.tsx
new file mode 100644
index 000000000..c6a189958
--- /dev/null
+++ b/src/button/demos/block.tsx
@@ -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 (
+
+
+
块级按钮
+
+
+
+
+
+
+
+
+
+
幽灵按钮
+
+
+
+
+ }>
+ Ghost with Icon
+
+
+
+
+ );
+}
diff --git a/src/button/demos/loading.tsx b/src/button/demos/loading.tsx
new file mode 100644
index 000000000..b3f7b61a2
--- /dev/null
+++ b/src/button/demos/loading.tsx
@@ -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 (
+
+
+
加载状态
+
+
+
+
+
+
+
+
+
+
点击后加载
+
+
+
+ }
+ loading={loading}
+ onClick={handleClick}
+ >
+ Click me!
+
+
+
+
+
+
危险按钮
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/button/index.md b/src/button/index.md
new file mode 100644
index 000000000..8fd25e568
--- /dev/null
+++ b/src/button/index.md
@@ -0,0 +1,34 @@
+---
+title: Button 按钮
+group: 组件
+toc: content
+demo:
+ cols: 2
+---
+
+# Button 按钮
+
+## 何时使用
+
+按钮用于开始一个即时操作。
+
+## 代码演示
+
+
+
+
+
+
+
+## API
+
+### Button
+
+Button 组件支持 antd Button 组件的所有属性,详见 [Ant Design Button API](https://ant.design/components/button-cn/#API)。
+
+## 注意事项
+
+- 使用自定义图标时,请确保图标组件符合规范,建议使用项目内的图标组件。
+- 图标大小会根据按钮的 `size` 属性自动调整,也可以通过设置图标组件的 `style` 属性来手动调整大小。
+- 当只设置 `icon` 而不设置 `children` 时,按钮将只显示图标。
+- 本组件是对 antd Button 的封装,支持 antd Button 的所有属性和事件。
diff --git a/src/button/index.scss b/src/button/index.scss
new file mode 100644
index 000000000..e70ec5182
--- /dev/null
+++ b/src/button/index.scss
@@ -0,0 +1,48 @@
+.dtc-button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ &__icon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ font-size: inherit;
+ line-height: 0;
+ // 因为 dtstack-icon 组件的 font-size 是 16px,现在需要继承 Button 组件的 font-size
+ .dtstack-icon {
+ font-size: inherit;
+ }
+ &--small {
+ font-size: 12px;
+ }
+ &--middle {
+ font-size: 16px;
+ }
+ &--large {
+ font-size: 18px;
+ }
+ & + .dtc-button__text {
+ &--small {
+ margin-left: 2px;
+ }
+ &--middle {
+ margin-left: 4px;
+ }
+ &--large {
+ margin-left: 8px;
+ }
+ }
+ }
+ &__text {
+ display: inline-block;
+ &--small {
+ font-size: 12px;
+ }
+ &--middle {
+ font-size: 12px;
+ }
+ &--large {
+ font-size: 16px;
+ }
+ }
+}
diff --git a/src/button/index.tsx b/src/button/index.tsx
new file mode 100644
index 000000000..9fb9dc386
--- /dev/null
+++ b/src/button/index.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { Button as AntdButton, ButtonProps as AntdButtonProps } from 'antd';
+import classNames from 'classnames';
+
+import './index.scss';
+
+export interface ButtonProps extends AntdButtonProps {}
+
+export default function Button({
+ className,
+ icon,
+ children,
+ size = 'middle',
+ ...rest
+}: ButtonProps) {
+ return (
+
+ {icon && {icon}}
+ {children && (
+ {children}
+ )}
+
+ );
+}
diff --git a/src/index.ts b/src/index.ts
index 802dfcdb4..6650b3e86 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,4 +1,5 @@
export { default as BlockHeader } from './blockHeader';
+export { default as Button } from './button';
export { default as Catalogue } from './catalogue';
export { default as Chat } from './chat';
export { default as CollapsibleActionItems } from './collapsibleActionItems';
From 393345a5ca455794cfabd5d1ae50a91027b75741 Mon Sep 17 00:00:00 2001
From: LuckyFBB <976060700@qq.com>
Date: Tue, 29 Jul 2025 14:11:59 +0800
Subject: [PATCH 2/2] test(button): change button unit test
---
.../__snapshots__/index.test.tsx.snap | 98 +++++++++++++++++++
src/button/__tests__/index.test.tsx | 51 +++++-----
2 files changed, 124 insertions(+), 25 deletions(-)
create mode 100644 src/button/__tests__/__snapshots__/index.test.tsx.snap
diff --git a/src/button/__tests__/__snapshots__/index.test.tsx.snap b/src/button/__tests__/__snapshots__/index.test.tsx.snap
new file mode 100644
index 000000000..1c9e342eb
--- /dev/null
+++ b/src/button/__tests__/__snapshots__/index.test.tsx.snap
@@ -0,0 +1,98 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Button should support contentLayout success render 1`] = `
+{
+ "asFragment": [Function],
+ "baseElement":
+
+
+
+ ,
+ "container":
+
+
,
+ "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],
+}
+`;
diff --git a/src/button/__tests__/index.test.tsx b/src/button/__tests__/index.test.tsx
index 56a982832..6fb4df277 100644
--- a/src/button/__tests__/index.test.tsx
+++ b/src/button/__tests__/index.test.tsx
@@ -1,46 +1,47 @@
import React from 'react';
import { UploadOutlined } from '@dtinsight/react-icons';
-import { render, screen } from '@testing-library/react';
+import { render } from '@testing-library/react';
+import '@testing-library/jest-dom/extend-expect';
-import Button from '../index';
+import Button from '..';
describe('Button', () => {
- it('renders button with text correctly', () => {
- render();
- expect(screen.getByText('Test Button')).toBeInTheDocument();
+ test('should support contentLayout success render', () => {
+ const wrapper = render(}>Primary);
+ expect(wrapper).toMatchSnapshot();
});
- it('renders button with icon correctly', () => {
- const { container } = render(}>With Icon);
- expect(container.querySelector('.dtc-button__icon')).toBeInTheDocument();
- expect(screen.getByText('With Icon')).toBeInTheDocument();
+ it('renders text correctly', () => {
+ const { getByText } = render();
+ expect(getByText('Hello')).toBeInTheDocument();
});
- it('renders icon-only button correctly', () => {
+ it('renders icon correctly', () => {
const { container } = render(} />);
expect(container.querySelector('.dtc-button__icon')).toBeInTheDocument();
- expect(container.querySelector('.dtc-button__text')).toBeNull();
+ expect(container.querySelector('.dtc-button__text')).not.toBeInTheDocument();
});
- it('applies custom className correctly', () => {
- const { container } = render();
- expect(container.querySelector('.dtc-button.custom-class')).toBeInTheDocument();
+ it('renders icon and text correctly', () => {
+ const { getByText, container } = render(}>Search);
+ expect(getByText('Search')).toBeInTheDocument();
+ expect(container.querySelector('.dtc-button__icon')).toBeInTheDocument();
});
- it('passes other props to antd Button', () => {
- render(
-
- );
- const button = screen.getByTestId('primary-button');
- expect(button).toHaveClass('ant-btn-primary');
+ it('applies custom className', () => {
+ const { container } = render();
+ expect(container.firstChild).toHaveClass('custom-class');
+ });
+
+ it('passes other props to AntdButton', () => {
+ const { getByText } = render();
+ expect(getByText('Primary').parentNode).toHaveClass('ant-btn-primary');
});
- it('applies correct size to icon and text', () => {
+ it('applies size class to icon and text', () => {
const { container } = render(
- }>
- Small Button
+ } size="small">
+ Test
);
expect(container.querySelector('.dtc-button__icon--small')).toBeInTheDocument();