diff --git a/src/index.ts b/src/index.ts index 613a09772..e76350807 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,6 +27,7 @@ export { default as KeyEventListener } from './keyEventListener'; export { default as MarkdownRender } from './markdownRender'; export { default as Modal } from './modal/modal'; export { default as NotFound } from './notFound'; +export { default as Popconfirm } from './popConfirm'; export { default as ProgressBar } from './progressBar'; export { default as ProgressLine } from './progressLine'; export { default as Resize } from './resize'; diff --git a/src/popConfirm/__tests__/__snapshots__/index.test.tsx.snap b/src/popConfirm/__tests__/__snapshots__/index.test.tsx.snap new file mode 100644 index 000000000..d5982c531 --- /dev/null +++ b/src/popConfirm/__tests__/__snapshots__/index.test.tsx.snap @@ -0,0 +1,62 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Popconfirm should render Popconfirm success render 1`] = ` +
+
+
+ +
+ +
+
+`; diff --git a/src/popConfirm/__tests__/index.test.tsx b/src/popConfirm/__tests__/index.test.tsx new file mode 100644 index 000000000..38b7ff23c --- /dev/null +++ b/src/popConfirm/__tests__/index.test.tsx @@ -0,0 +1,121 @@ +import React from 'react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; + +import Popconfirm from '../index'; + +describe('Popconfirm', () => { + test('should render Popconfirm success render', async () => { + render(Click me); + fireEvent.click(screen.getByText('Click me')); + expect(screen.getByText('Are you sure?')).toBeInTheDocument(); + await waitFor(() => + expect(document.querySelector('.ant-popover-message')?.firstChild).toHaveAttribute( + 'data-mock-icon', + 'InformationFilled' + ) + ); + await waitFor(() => expect(document.querySelector('.ant-popover')).toMatchSnapshot()); + await waitFor(() => expect(document.querySelector('.dtc-popconfirm')).toBeInTheDocument()); + }); + + it('should show overlay when trigger is clicked', async () => { + const popconfirm = render( + + show me your code + + ); + + expect(popconfirm.container.querySelector('.ant-popover')).toBe(null); + + const triggerNode = popconfirm.container.querySelectorAll('span')[0]; + fireEvent.click(triggerNode); + + await waitFor(() => expect(document.querySelector('.ant-popover')).not.toBeNull()); + }); + + it('should render correctly with warning type and warning icon', async () => { + render( + + Click me + + ); + fireEvent.click(screen.getByText('Click me')); + await waitFor(() => expect(screen.getByText('Warning!')).toBeInTheDocument()); + await waitFor(() => + expect(document.querySelector('.ant-popover-message')?.firstChild).toHaveAttribute( + 'data-mock-icon', + 'WarningFilled' + ) + ); + }); + + it('should render correctly with danger type and close icon', async () => { + render( + + Click me + + ); + fireEvent.click(screen.getByText('Click me')); + await waitFor(() => expect(screen.getByText('Danger!')).toBeInTheDocument()); + }); + + it('should not render icon when showIcon is false', async () => { + render( + + Click me + + ); + fireEvent.click(screen.getByText('Click me')); + await waitFor(() => expect(screen.getByText('No Icon')).toBeInTheDocument()); + await waitFor(() => + expect(document.querySelector('.ant-popover-message')?.children.length).toBe(1) + ); + }); + + it('should use custom icon when provided', async () => { + const CustomIcon = () =>
Custom
; + render( + }> + Click me + + ); + fireEvent.click(screen.getByText('Click me')); + await waitFor(() => expect(screen.getByTestId('custom-icon')).toBeInTheDocument()); + }); + + it('should trigger onConfirm when OK button is clicked', async () => { + const onConfirm = jest.fn(); + render( + + Click me + + ); + fireEvent.click(screen.getByText('Click me')); + fireEvent.click(await screen.findByText('OK')); + expect(onConfirm).toHaveBeenCalled(); + }); + + it('should trigger onCancel when Cancel button is clicked', async () => { + const onCancel = jest.fn(); + render( + + Click me + + ); + fireEvent.click(screen.getByText('Click me')); + fireEvent.click(await screen.findByText('Cancel')); + expect(onCancel).toHaveBeenCalled(); + }); + + it('should apply danger style to OK button for danger type', async () => { + render( + + Click me + + ); + fireEvent.click(screen.getByText('Click me')); + const okButton = await screen.findByText('OK'); + expect(okButton.closest('button')).toHaveClass('ant-btn-dangerous'); + }); +}); diff --git a/src/popConfirm/demos/basic.tsx b/src/popConfirm/demos/basic.tsx new file mode 100644 index 000000000..663b13842 --- /dev/null +++ b/src/popConfirm/demos/basic.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Space } from 'antd'; +import { Popconfirm } from 'dt-react-component'; + +const App: React.FC = () => ( + + + Basic + + + 超长文本 + + +); + +export default App; diff --git a/src/popConfirm/demos/noIcon.tsx b/src/popConfirm/demos/noIcon.tsx new file mode 100644 index 000000000..75750ecda --- /dev/null +++ b/src/popConfirm/demos/noIcon.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { PlusCircleFilled } from '@dtinsight/react-icons'; +import { Space } from 'antd'; +import { Button, Popconfirm } from 'dt-react-component'; + +export default () => ( + + + + + }> + + + +); diff --git a/src/popConfirm/demos/type.tsx b/src/popConfirm/demos/type.tsx new file mode 100644 index 000000000..2f6da334a --- /dev/null +++ b/src/popConfirm/demos/type.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Space } from 'antd'; +import { Button, Popconfirm } from 'dt-react-component'; + +export default () => ( + + + + + + + + + + + +); diff --git a/src/popConfirm/index.md b/src/popConfirm/index.md new file mode 100644 index 000000000..235dd2a6e --- /dev/null +++ b/src/popConfirm/index.md @@ -0,0 +1,24 @@ +--- +title: Popconfirm 气泡确认框 +group: 组件 +toc: content +--- + +# Popconfirm 气泡确认框 + +对操作进行二次确认,支持自定义图标和类型。 + +## 示例 + + + + + +## API + +| 属性 | 说明 | 类型 | 默认值 | +| -------- | ------------ | ---------------------------------- | --------- | +| showIcon | 是否显示图标 | boolean | true | +| type | 图标类型 | 'primary' \| 'warning' \| 'danger' | 'primary' | + +其余属性均继承自 `Popconfirm` 组件,参考 [Popconfirm API](https://4x.ant.design/components/popconfirm-cn/#API) diff --git a/src/popConfirm/index.scss b/src/popConfirm/index.scss new file mode 100644 index 000000000..c1af14e89 --- /dev/null +++ b/src/popConfirm/index.scss @@ -0,0 +1,13 @@ +.dtc-popconfirm { + .ant-popover-message { + display: flex; + align-items: start; + &-title { + padding-left: 0; + } + } + .dtstack-icon { + margin-right: 8px; + font-size: 20px; + } +} diff --git a/src/popConfirm/index.tsx b/src/popConfirm/index.tsx new file mode 100644 index 000000000..9371bb94f --- /dev/null +++ b/src/popConfirm/index.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { CloseFilled, InformationFilled, WarningFilled } from '@dtinsight/react-icons'; +import { Popconfirm as AntdPopconfirm, PopconfirmProps as AntdPopconfirmProps } from 'antd'; +import classNames from 'classnames'; + +import './index.scss'; + +export interface PopconfirmProps extends AntdPopconfirmProps { + showIcon?: boolean; + type?: 'primary' | 'warning' | 'danger'; +} + +const Popconfirm = ({ + showIcon = true, + type = 'primary', + icon, + okButtonProps, + ...rest +}: PopconfirmProps) => { + const generateIcon = () => { + if (!showIcon) return <>; + if (icon) return icon; + switch (type) { + case 'primary': + return ; + case 'warning': + return ; + case 'danger': + return ; + default: + return <>; + } + }; + + return ( + + ); +}; + +export default Popconfirm;