From e18c3c44336f19771fcef764b8f8756d4f8d5f56 Mon Sep 17 00:00:00 2001 From: LuckyFBB <976060700@qq.com> Date: Tue, 22 Jul 2025 13:58:03 +0800 Subject: [PATCH 1/2] feat(button): refactor antd button, rewrite icon #584 --- src/button/__tests__/index.test.tsx | 49 +++++++++++++++++ src/button/demos/basic.tsx | 82 +++++++++++++++++++++++++++++ src/button/demos/block.tsx | 42 +++++++++++++++ src/button/demos/loading.tsx | 71 +++++++++++++++++++++++++ src/button/index.md | 34 ++++++++++++ src/button/index.scss | 48 +++++++++++++++++ src/button/index.tsx | 24 +++++++++ src/index.ts | 1 + 8 files changed, 351 insertions(+) create mode 100644 src/button/__tests__/index.test.tsx create mode 100644 src/button/demos/basic.tsx create mode 100644 src/button/demos/block.tsx create mode 100644 src/button/demos/loading.tsx create mode 100644 src/button/index.md create mode 100644 src/button/index.scss create mode 100644 src/button/index.tsx diff --git a/src/button/__tests__/index.test.tsx b/src/button/__tests__/index.test.tsx new file mode 100644 index 000000000..56a982832 --- /dev/null +++ b/src/button/__tests__/index.test.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { UploadOutlined } from '@dtinsight/react-icons'; +import { render, screen } from '@testing-library/react'; + +import Button from '../index'; + +describe('Button', () => { + it('renders button with text correctly', () => { + render(); + expect(screen.getByText('Test Button')).toBeInTheDocument(); + }); + + it('renders button with icon correctly', () => { + const { container } = render(); + expect(container.querySelector('.dtc-button__icon')).toBeInTheDocument(); + expect(screen.getByText('With Icon')).toBeInTheDocument(); + }); + + it('renders icon-only button correctly', () => { + const { container } = render(); + expect(container.querySelector('.dtc-button.custom-class')).toBeInTheDocument(); + }); + + it('passes other props to antd Button', () => { + render( + + ); + const button = screen.getByTestId('primary-button'); + expect(button).toHaveClass('ant-btn-primary'); + }); + + it('applies correct size to icon and text', () => { + const { container } = render( + + ); + expect(container.querySelector('.dtc-button__icon--small')).toBeInTheDocument(); + expect(container.querySelector('.dtc-button__text--small')).toBeInTheDocument(); + }); +}); diff --git a/src/button/demos/basic.tsx b/src/button/demos/basic.tsx new file mode 100644 index 000000000..002a8face --- /dev/null +++ b/src/button/demos/basic.tsx @@ -0,0 +1,82 @@ +import React, { useState } from 'react'; +import { UploadOutlined } from '@dtinsight/react-icons'; +import { Radio, Space } from 'antd'; +import { Button } from 'dt-react-component'; + +import { ButtonProps } from '..'; + +export default function Basic() { + const [size, setSize] = useState('large'); + + return ( + + setSize(e.target.value)}> + Large + Default + Small + + +
+

按钮类型

+ + + + + + +
+ +
+

带图标的按钮

+ + + + + + +
+ +
+

纯图标按钮

+ +
+ +
+

禁用状态

+ + + + + + +
+
+ ); +} diff --git a/src/button/demos/block.tsx b/src/button/demos/block.tsx new file mode 100644 index 000000000..c6a189958 --- /dev/null +++ b/src/button/demos/block.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { UploadOutlined } from '@dtinsight/react-icons'; +import { Space } from 'antd'; +import { Button } from 'dt-react-component'; + +export default function BlockDemo() { + return ( + +
+

块级按钮

+ + + + + + +
+ +
+

幽灵按钮

+ + + + + + +
+
+ ); +} diff --git a/src/button/demos/loading.tsx b/src/button/demos/loading.tsx new file mode 100644 index 000000000..b3f7b61a2 --- /dev/null +++ b/src/button/demos/loading.tsx @@ -0,0 +1,71 @@ +import React, { useState } from 'react'; +import { UploadOutlined } from '@dtinsight/react-icons'; +import { Space } from 'antd'; +import { Button } from 'dt-react-component'; + +export default function LoadingDemo() { + const [loading, setLoading] = useState(false); + + const handleClick = () => { + setLoading(true); + setTimeout(() => { + setLoading(false); + }, 2000); + }; + + return ( + +
+

加载状态

+ + + + + + +
+ +
+

点击后加载

+ + + + + +
+ +
+

危险按钮

+ + + + + + +
+
+ ); +} diff --git a/src/button/index.md b/src/button/index.md new file mode 100644 index 000000000..8fd25e568 --- /dev/null +++ b/src/button/index.md @@ -0,0 +1,34 @@ +--- +title: Button 按钮 +group: 组件 +toc: content +demo: + cols: 2 +--- + +# Button 按钮 + +## 何时使用 + +按钮用于开始一个即时操作。 + +## 代码演示 + + + + + + + +## API + +### Button + +Button 组件支持 antd Button 组件的所有属性,详见 [Ant Design Button API](https://ant.design/components/button-cn/#API)。 + +## 注意事项 + +- 使用自定义图标时,请确保图标组件符合规范,建议使用项目内的图标组件。 +- 图标大小会根据按钮的 `size` 属性自动调整,也可以通过设置图标组件的 `style` 属性来手动调整大小。 +- 当只设置 `icon` 而不设置 `children` 时,按钮将只显示图标。 +- 本组件是对 antd Button 的封装,支持 antd Button 的所有属性和事件。 diff --git a/src/button/index.scss b/src/button/index.scss new file mode 100644 index 000000000..e70ec5182 --- /dev/null +++ b/src/button/index.scss @@ -0,0 +1,48 @@ +.dtc-button { + display: inline-flex; + align-items: center; + justify-content: center; + &__icon { + display: inline-flex; + align-items: center; + justify-content: center; + font-size: inherit; + line-height: 0; + // 因为 dtstack-icon 组件的 font-size 是 16px,现在需要继承 Button 组件的 font-size + .dtstack-icon { + font-size: inherit; + } + &--small { + font-size: 12px; + } + &--middle { + font-size: 16px; + } + &--large { + font-size: 18px; + } + & + .dtc-button__text { + &--small { + margin-left: 2px; + } + &--middle { + margin-left: 4px; + } + &--large { + margin-left: 8px; + } + } + } + &__text { + display: inline-block; + &--small { + font-size: 12px; + } + &--middle { + font-size: 12px; + } + &--large { + font-size: 16px; + } + } +} diff --git a/src/button/index.tsx b/src/button/index.tsx new file mode 100644 index 000000000..9fb9dc386 --- /dev/null +++ b/src/button/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { Button as AntdButton, ButtonProps as AntdButtonProps } from 'antd'; +import classNames from 'classnames'; + +import './index.scss'; + +export interface ButtonProps extends AntdButtonProps {} + +export default function Button({ + className, + icon, + children, + size = 'middle', + ...rest +}: ButtonProps) { + return ( + + {icon && {icon}} + {children && ( + {children} + )} + + ); +} diff --git a/src/index.ts b/src/index.ts index 802dfcdb4..6650b3e86 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ export { default as BlockHeader } from './blockHeader'; +export { default as Button } from './button'; export { default as Catalogue } from './catalogue'; export { default as Chat } from './chat'; export { default as CollapsibleActionItems } from './collapsibleActionItems'; From 393345a5ca455794cfabd5d1ae50a91027b75741 Mon Sep 17 00:00:00 2001 From: LuckyFBB <976060700@qq.com> Date: Tue, 29 Jul 2025 14:11:59 +0800 Subject: [PATCH 2/2] test(button): change button unit test --- .../__snapshots__/index.test.tsx.snap | 98 +++++++++++++++++++ src/button/__tests__/index.test.tsx | 51 +++++----- 2 files changed, 124 insertions(+), 25 deletions(-) create mode 100644 src/button/__tests__/__snapshots__/index.test.tsx.snap diff --git a/src/button/__tests__/__snapshots__/index.test.tsx.snap b/src/button/__tests__/__snapshots__/index.test.tsx.snap new file mode 100644 index 000000000..1c9e342eb --- /dev/null +++ b/src/button/__tests__/__snapshots__/index.test.tsx.snap @@ -0,0 +1,98 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Button should support contentLayout success render 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ +
+ , + "container":
+ +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/src/button/__tests__/index.test.tsx b/src/button/__tests__/index.test.tsx index 56a982832..6fb4df277 100644 --- a/src/button/__tests__/index.test.tsx +++ b/src/button/__tests__/index.test.tsx @@ -1,46 +1,47 @@ import React from 'react'; import { UploadOutlined } from '@dtinsight/react-icons'; -import { render, screen } from '@testing-library/react'; +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; -import Button from '../index'; +import Button from '..'; describe('Button', () => { - it('renders button with text correctly', () => { - render(); - expect(screen.getByText('Test Button')).toBeInTheDocument(); + test('should support contentLayout success render', () => { + const wrapper = render(); + expect(wrapper).toMatchSnapshot(); }); - it('renders button with icon correctly', () => { - const { container } = render(); - expect(container.querySelector('.dtc-button__icon')).toBeInTheDocument(); - expect(screen.getByText('With Icon')).toBeInTheDocument(); + it('renders text correctly', () => { + const { getByText } = render(); + expect(getByText('Hello')).toBeInTheDocument(); }); - it('renders icon-only button correctly', () => { + it('renders icon correctly', () => { const { container } = render(); - expect(container.querySelector('.dtc-button.custom-class')).toBeInTheDocument(); + it('renders icon and text correctly', () => { + const { getByText, container } = render(); + expect(getByText('Search')).toBeInTheDocument(); + expect(container.querySelector('.dtc-button__icon')).toBeInTheDocument(); }); - it('passes other props to antd Button', () => { - render( - - ); - const button = screen.getByTestId('primary-button'); - expect(button).toHaveClass('ant-btn-primary'); + it('applies custom className', () => { + const { container } = render(); + expect(container.firstChild).toHaveClass('custom-class'); + }); + + it('passes other props to AntdButton', () => { + const { getByText } = render(); + expect(getByText('Primary').parentNode).toHaveClass('ant-btn-primary'); }); - it('applies correct size to icon and text', () => { + it('applies size class to icon and text', () => { const { container } = render( - ); expect(container.querySelector('.dtc-button__icon--small')).toBeInTheDocument();