Skip to content

Commit fd4385f

Browse files
authored
feat(popconfirm): support popconfirm (#599)
1 parent 3b47ed7 commit fd4385f

File tree

9 files changed

+317
-0
lines changed

9 files changed

+317
-0
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export { default as zhCN } from './locale/zh-CN';
3131
export { default as MarkdownRender } from './markdownRender';
3232
export { default as Modal } from './modal/modal';
3333
export { default as NotFound } from './notFound';
34+
export { default as Popconfirm } from './popConfirm';
3435
export { default as ProgressBar } from './progressBar';
3536
export { default as ProgressLine } from './progressLine';
3637
export { default as Resize } from './resize';
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Popconfirm should render Popconfirm success render 1`] = `
4+
<div
5+
class="ant-popover ant-popconfirm dtc-popconfirm"
6+
style="opacity: 0;"
7+
>
8+
<div
9+
class="ant-popover-content"
10+
>
11+
<div
12+
class="ant-popover-arrow"
13+
>
14+
<span
15+
class="ant-popover-arrow-content"
16+
/>
17+
</div>
18+
<div
19+
class="ant-popover-inner"
20+
role="tooltip"
21+
>
22+
<div
23+
class="ant-popover-inner-content"
24+
>
25+
<div
26+
class="ant-popover-message"
27+
>
28+
<span
29+
color="#1D78FF"
30+
data-mock-icon="InformationFilled"
31+
/>
32+
<div
33+
class="ant-popover-message-title"
34+
>
35+
Are you sure?
36+
</div>
37+
</div>
38+
<div
39+
class="ant-popover-buttons"
40+
>
41+
<button
42+
class="ant-btn ant-btn-default ant-btn-sm"
43+
type="button"
44+
>
45+
<span>
46+
Cancel
47+
</span>
48+
</button>
49+
<button
50+
class="ant-btn ant-btn-primary ant-btn-sm"
51+
type="button"
52+
>
53+
<span>
54+
OK
55+
</span>
56+
</button>
57+
</div>
58+
</div>
59+
</div>
60+
</div>
61+
</div>
62+
`;
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import React from 'react';
2+
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
3+
import '@testing-library/jest-dom/extend-expect';
4+
5+
import Popconfirm from '../index';
6+
7+
describe('Popconfirm', () => {
8+
test('should render Popconfirm success render', async () => {
9+
render(<Popconfirm title="Are you sure?">Click me</Popconfirm>);
10+
fireEvent.click(screen.getByText('Click me'));
11+
expect(screen.getByText('Are you sure?')).toBeInTheDocument();
12+
await waitFor(() =>
13+
expect(document.querySelector('.ant-popover-message')?.firstChild).toHaveAttribute(
14+
'data-mock-icon',
15+
'InformationFilled'
16+
)
17+
);
18+
await waitFor(() => expect(document.querySelector('.ant-popover')).toMatchSnapshot());
19+
await waitFor(() => expect(document.querySelector('.dtc-popconfirm')).toBeInTheDocument());
20+
});
21+
22+
it('should show overlay when trigger is clicked', async () => {
23+
const popconfirm = render(
24+
<Popconfirm title="code" trigger="click" autoAdjustOverflow={false}>
25+
<span>show me your code</span>
26+
</Popconfirm>
27+
);
28+
29+
expect(popconfirm.container.querySelector('.ant-popover')).toBe(null);
30+
31+
const triggerNode = popconfirm.container.querySelectorAll('span')[0];
32+
fireEvent.click(triggerNode);
33+
34+
await waitFor(() => expect(document.querySelector('.ant-popover')).not.toBeNull());
35+
});
36+
37+
it('should render correctly with warning type and warning icon', async () => {
38+
render(
39+
<Popconfirm title="Warning!" type="warning">
40+
Click me
41+
</Popconfirm>
42+
);
43+
fireEvent.click(screen.getByText('Click me'));
44+
await waitFor(() => expect(screen.getByText('Warning!')).toBeInTheDocument());
45+
await waitFor(() =>
46+
expect(document.querySelector('.ant-popover-message')?.firstChild).toHaveAttribute(
47+
'data-mock-icon',
48+
'WarningFilled'
49+
)
50+
);
51+
});
52+
53+
it('should render correctly with danger type and close icon', async () => {
54+
render(
55+
<Popconfirm title="Danger!" type="danger">
56+
Click me
57+
</Popconfirm>
58+
);
59+
fireEvent.click(screen.getByText('Click me'));
60+
await waitFor(() => expect(screen.getByText('Danger!')).toBeInTheDocument());
61+
});
62+
63+
it('should not render icon when showIcon is false', async () => {
64+
render(
65+
<Popconfirm title="No Icon" showIcon={false}>
66+
Click me
67+
</Popconfirm>
68+
);
69+
fireEvent.click(screen.getByText('Click me'));
70+
await waitFor(() => expect(screen.getByText('No Icon')).toBeInTheDocument());
71+
await waitFor(() =>
72+
expect(document.querySelector('.ant-popover-message')?.children.length).toBe(1)
73+
);
74+
});
75+
76+
it('should use custom icon when provided', async () => {
77+
const CustomIcon = () => <div data-testid="custom-icon">Custom</div>;
78+
render(
79+
<Popconfirm title="Custom Icon" icon={<CustomIcon />}>
80+
Click me
81+
</Popconfirm>
82+
);
83+
fireEvent.click(screen.getByText('Click me'));
84+
await waitFor(() => expect(screen.getByTestId('custom-icon')).toBeInTheDocument());
85+
});
86+
87+
it('should trigger onConfirm when OK button is clicked', async () => {
88+
const onConfirm = jest.fn();
89+
render(
90+
<Popconfirm title="Confirm?" onConfirm={onConfirm}>
91+
Click me
92+
</Popconfirm>
93+
);
94+
fireEvent.click(screen.getByText('Click me'));
95+
fireEvent.click(await screen.findByText('OK'));
96+
expect(onConfirm).toHaveBeenCalled();
97+
});
98+
99+
it('should trigger onCancel when Cancel button is clicked', async () => {
100+
const onCancel = jest.fn();
101+
render(
102+
<Popconfirm title="Cancel?" onCancel={onCancel}>
103+
Click me
104+
</Popconfirm>
105+
);
106+
fireEvent.click(screen.getByText('Click me'));
107+
fireEvent.click(await screen.findByText('Cancel'));
108+
expect(onCancel).toHaveBeenCalled();
109+
});
110+
111+
it('should apply danger style to OK button for danger type', async () => {
112+
render(
113+
<Popconfirm title="Danger!" type="danger">
114+
Click me
115+
</Popconfirm>
116+
);
117+
fireEvent.click(screen.getByText('Click me'));
118+
const okButton = await screen.findByText('OK');
119+
expect(okButton.closest('button')).toHaveClass('ant-btn-dangerous');
120+
});
121+
});

src/popConfirm/demos/basic.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import { Space } from 'antd';
3+
import { Popconfirm } from 'dt-react-component';
4+
5+
const App: React.FC = () => (
6+
<Space size={12}>
7+
<Popconfirm title="Are you sure to delete this task?">
8+
<a href="#">Basic</a>
9+
</Popconfirm>
10+
<Popconfirm
11+
title="超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本"
12+
overlayInnerStyle={{ width: 400 }}
13+
>
14+
<a href="#">超长文本</a>
15+
</Popconfirm>
16+
</Space>
17+
);
18+
19+
export default App;

src/popConfirm/demos/noIcon.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react';
2+
import { PlusCircleFilled } from '@dtinsight/react-icons';
3+
import { Space } from 'antd';
4+
import { Button, Popconfirm } from 'dt-react-component';
5+
6+
export default () => (
7+
<Space>
8+
<Popconfirm title="没有图标" showIcon={false}>
9+
<Button>无图标确认</Button>
10+
</Popconfirm>
11+
<Popconfirm title="警告操作" icon={<PlusCircleFilled />}>
12+
<Button type="default">自定义icon</Button>
13+
</Popconfirm>
14+
</Space>
15+
);

src/popConfirm/demos/type.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
import { Space } from 'antd';
3+
import { Button, Popconfirm } from 'dt-react-component';
4+
5+
export default () => (
6+
<Space>
7+
<Popconfirm title="主操作" type="primary">
8+
<Button type="primary">主操作</Button>
9+
</Popconfirm>
10+
<Popconfirm title="警告操作" type="warning">
11+
<Button type="default">警告</Button>
12+
</Popconfirm>
13+
<Popconfirm title="危险操作" type="danger">
14+
<Button danger>危险</Button>
15+
</Popconfirm>
16+
</Space>
17+
);

src/popConfirm/index.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
title: Popconfirm 气泡确认框
3+
group: 组件
4+
toc: content
5+
---
6+
7+
# Popconfirm 气泡确认框
8+
9+
对操作进行二次确认,支持自定义图标和类型。
10+
11+
## 示例
12+
13+
<code src="./demos/basic.tsx" title="基础"></code>
14+
<code src="./demos/type.tsx" title="类型"></code>
15+
<code src="./demos/noIcon.tsx" title="没有图标"></code>
16+
17+
## API
18+
19+
| 属性 | 说明 | 类型 | 默认值 |
20+
| -------- | ------------ | ---------------------------------- | --------- |
21+
| showIcon | 是否显示图标 | boolean | true |
22+
| type | 图标类型 | 'primary' \| 'warning' \| 'danger' | 'primary' |
23+
24+
其余属性均继承自 `Popconfirm` 组件,参考 [Popconfirm API](https://4x.ant.design/components/popconfirm-cn/#API)

src/popConfirm/index.scss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.dtc-popconfirm {
2+
.ant-popover-message {
3+
display: flex;
4+
align-items: start;
5+
&-title {
6+
padding-left: 0;
7+
}
8+
}
9+
.dtstack-icon {
10+
margin-right: 8px;
11+
font-size: 20px;
12+
}
13+
}

src/popConfirm/index.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from 'react';
2+
import { CloseFilled, InformationFilled, WarningFilled } from '@dtinsight/react-icons';
3+
import { Popconfirm as AntdPopconfirm, PopconfirmProps as AntdPopconfirmProps } from 'antd';
4+
import classNames from 'classnames';
5+
6+
import './index.scss';
7+
8+
export interface PopconfirmProps extends AntdPopconfirmProps {
9+
showIcon?: boolean;
10+
type?: 'primary' | 'warning' | 'danger';
11+
}
12+
13+
const Popconfirm = ({
14+
showIcon = true,
15+
type = 'primary',
16+
icon,
17+
okButtonProps,
18+
...rest
19+
}: PopconfirmProps) => {
20+
const generateIcon = () => {
21+
if (!showIcon) return <></>;
22+
if (icon) return icon;
23+
switch (type) {
24+
case 'primary':
25+
return <InformationFilled color="#1D78FF" />;
26+
case 'warning':
27+
return <WarningFilled color="#FBB310" />;
28+
case 'danger':
29+
return <CloseFilled color="#F96C5B" />;
30+
default:
31+
return <></>;
32+
}
33+
};
34+
35+
return (
36+
<AntdPopconfirm
37+
overlayClassName={classNames('dtc-popconfirm')}
38+
icon={generateIcon()}
39+
okButtonProps={{ danger: type === 'danger', ...okButtonProps }}
40+
{...rest}
41+
/>
42+
);
43+
};
44+
45+
export default Popconfirm;

0 commit comments

Comments
 (0)