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 ( + + + + + + ); +} 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; }