From ecdf75014b788e2e2a45c0176b2c558baf4f97ce Mon Sep 17 00:00:00 2001
From: LuckyFBB <976060700@qq.com>
Date: Wed, 9 Sep 2026 11:32:59 +0800
Subject: [PATCH] feat(button): add tooltip support for button
#650
---
src/button/__tests__/index.test.tsx | 85 ++++++++++++++++++++++++++++-
src/button/demos/tooltip.tsx | 23 ++++++++
src/button/index.md | 7 +++
src/button/index.tsx | 15 ++++-
4 files changed, 126 insertions(+), 4 deletions(-)
create mode 100644 src/button/demos/tooltip.tsx
diff --git a/src/button/__tests__/index.test.tsx b/src/button/__tests__/index.test.tsx
index 7e1378a2c..e09d76e25 100644
--- a/src/button/__tests__/index.test.tsx
+++ b/src/button/__tests__/index.test.tsx
@@ -1,6 +1,7 @@
import React from 'react';
+import { act } from 'react-dom/test-utils';
import { UploadOutlined } from '@dtinsight/react-icons';
-import { render } from '@testing-library/react';
+import { fireEvent, render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import Button from '..';
@@ -52,4 +53,86 @@ describe('Button', () => {
expect(container.querySelector('.dtc-button__icon--small')).toBeInTheDocument();
expect(container.querySelector('.dtc-button__text--small')).toBeInTheDocument();
});
+
+ it('shows tooltip from ReactNode content', () => {
+ jest.useFakeTimers();
+ const { getByRole } = render();
+
+ act(() => {
+ fireEvent.mouseEnter(getByRole('button'));
+ jest.runAllTimers();
+ });
+
+ expect(document.body.querySelector('.ant-tooltip-inner')).toHaveTextContent('Upload file');
+ jest.useRealTimers();
+ });
+
+ it('supports tooltip props', () => {
+ jest.useFakeTimers();
+ const { getByRole } = render(
+
+ );
+
+ act(() => {
+ fireEvent.mouseEnter(getByRole('button'));
+ jest.runAllTimers();
+ });
+
+ expect(document.body.querySelector('.custom-button-tooltip')).toBeInTheDocument();
+ jest.useRealTimers();
+ });
+
+ it('shows tooltip for a disabled button', () => {
+ jest.useFakeTimers();
+ const { getByRole } = render(
+
+ );
+ const button = getByRole('button');
+ const tooltipTrigger = button.parentElement;
+
+ expect(tooltipTrigger).toHaveClass('ant-tooltip-disabled-compatible-wrapper');
+ if (!tooltipTrigger) throw new Error('Tooltip trigger should exist');
+ act(() => {
+ fireEvent.mouseEnter(tooltipTrigger);
+ jest.runAllTimers();
+ });
+
+ expect(document.body.querySelector('.ant-tooltip-inner')).toHaveTextContent(
+ 'No permission'
+ );
+ jest.useRealTimers();
+ });
+
+ it.each([undefined, null, false, ''] as const)(
+ 'keeps the original structure when tooltip is %p',
+ (tooltip) => {
+ const { container } = render();
+
+ expect(container.firstElementChild).toBe(container.querySelector('button'));
+ expect(container.querySelector('button')).not.toHaveAttribute('tooltip');
+ }
+ );
+
+ it('treats zero as valid tooltip content', () => {
+ jest.useFakeTimers();
+ const { getByRole } = render();
+
+ act(() => {
+ fireEvent.mouseEnter(getByRole('button'));
+ jest.runAllTimers();
+ });
+
+ expect(document.body.querySelector('.ant-tooltip-inner')).toHaveTextContent('0');
+ jest.useRealTimers();
+ });
});
diff --git a/src/button/demos/tooltip.tsx b/src/button/demos/tooltip.tsx
new file mode 100644
index 000000000..fe9e3a158
--- /dev/null
+++ b/src/button/demos/tooltip.tsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import { DeleteOutlined, UploadOutlined } from '@dtinsight/react-icons';
+import { Space } from 'antd';
+import { Button } from 'dt-react-component';
+
+export default function TooltipDemo() {
+ return (
+
+
+ } tooltip="上传文件" />
+
+ }
+ tooltip={{ title: '删除后无法恢复', placement: 'bottom' }}
+ >
+ 删除
+
+
+ );
+}
diff --git a/src/button/index.md b/src/button/index.md
index 8fd25e568..c9942caf6 100644
--- a/src/button/index.md
+++ b/src/button/index.md
@@ -20,15 +20,22 @@ demo:
+
+
## API
### Button
Button 组件支持 antd Button 组件的所有属性,详见 [Ant Design Button API](https://ant.design/components/button-cn/#API)。
+| 属性 | 说明 | 类型 | 默认值 |
+| ------- | ------------------------------------ | --------------------------------------- | ------ |
+| tooltip | 配置按钮的 Tooltip;不传时不展示提示 | `TooltipProps \| TooltipProps['title']` | - |
+
## 注意事项
- 使用自定义图标时,请确保图标组件符合规范,建议使用项目内的图标组件。
- 图标大小会根据按钮的 `size` 属性自动调整,也可以通过设置图标组件的 `style` 属性来手动调整大小。
- 当只设置 `icon` 而不设置 `children` 时,按钮将只显示图标。
+- `tooltip` 的展示规则及配置项与 antd Tooltip 一致;不建议同时设置 `tooltip` 和原生 `title`,以免展示两种提示。
- 本组件是对 antd Button 的封装,支持 antd Button 的所有属性和事件。
diff --git a/src/button/index.tsx b/src/button/index.tsx
index b18ac868f..147866f5a 100644
--- a/src/button/index.tsx
+++ b/src/button/index.tsx
@@ -1,19 +1,23 @@
import React from 'react';
-import { Button as AntdButton, ButtonProps as AntdButtonProps } from 'antd';
+import { Button as AntdButton, ButtonProps as AntdButtonProps, Tooltip } from 'antd';
import classNames from 'classnames';
+import { LabelTooltipType, toTooltipProps } from '../utils';
import './index.scss';
-export interface ButtonProps extends AntdButtonProps {}
+export interface ButtonProps extends AntdButtonProps {
+ tooltip?: LabelTooltipType;
+}
export default function Button({
className,
icon,
children,
size = 'middle',
+ tooltip,
...rest
}: ButtonProps) {
- return (
+ const buttonNode = (
{icon && {icon}}
{children && (
@@ -21,4 +25,9 @@ export default function Button({
)}
);
+
+ const tooltipProps = toTooltipProps(tooltip);
+ const hasTooltip = tooltipProps && (tooltipProps.title || tooltipProps.title === 0);
+
+ return hasTooltip ? {buttonNode} : buttonNode;
}