Skip to content
Open
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
85 changes: 84 additions & 1 deletion src/button/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -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 '..';
Expand Down Expand Up @@ -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(<Button tooltip="Upload file">Upload</Button>);

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(
<Button
tooltip={{
title: 'Upload file',
overlayClassName: 'custom-button-tooltip',
mouseEnterDelay: 0,
}}
>
Upload
</Button>
);

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(
<Button disabled tooltip={{ title: 'No permission', mouseEnterDelay: 0 }}>
Delete
</Button>
);
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(<Button tooltip={tooltip}>Upload</Button>);

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(<Button tooltip={0}>Count</Button>);

act(() => {
fireEvent.mouseEnter(getByRole('button'));
jest.runAllTimers();
});

expect(document.body.querySelector('.ant-tooltip-inner')).toHaveTextContent('0');
jest.useRealTimers();
});
});
23 changes: 23 additions & 0 deletions src/button/demos/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Space>
<Button tooltip="上传文件">上传</Button>
<Button icon={<UploadOutlined />} tooltip="上传文件" />
<Button disabled tooltip="暂无操作权限">
禁用按钮
</Button>
<Button
danger
icon={<DeleteOutlined />}
tooltip={{ title: '删除后无法恢复', placement: 'bottom' }}
>
删除
</Button>
</Space>
);
}
7 changes: 7 additions & 0 deletions src/button/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,22 @@ demo:

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

<code src="./demos/tooltip.tsx" title="文字提示" description="通过 tooltip 配置普通按钮、纯图标按钮和禁用按钮的文字提示。"></code>

## 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 的所有属性和事件。
15 changes: 12 additions & 3 deletions src/button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
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 = (
<AntdButton className={classNames('dtc-button', className)} size={size} {...rest}>
{icon && <span className={`dtc-button__icon dtc-button__icon--${size}`}>{icon}</span>}
{children && (
<span className={`dtc-button__text dtc-button__text--${size}`}>{children}</span>
)}
</AntdButton>
);

const tooltipProps = toTooltipProps(tooltip);
const hasTooltip = tooltipProps && (tooltipProps.title || tooltipProps.title === 0);

return hasTooltip ? <Tooltip {...tooltipProps}>{buttonNode}</Tooltip> : buttonNode;
}
Loading