diff --git a/src/blockHeader/__tests__/__snapshots__/blockHeader.test.tsx.snap b/src/blockHeader/__tests__/__snapshots__/blockHeader.test.tsx.snap index 1e4d81f03..21fbdb5b0 100644 --- a/src/blockHeader/__tests__/__snapshots__/blockHeader.test.tsx.snap +++ b/src/blockHeader/__tests__/__snapshots__/blockHeader.test.tsx.snap @@ -9,28 +9,25 @@ exports[`test BlockHeader render should render BlockHeader success render 1`] = class="dtc-block-header" >
标题1
-
, @@ -39,28 +36,25 @@ exports[`test BlockHeader render should render BlockHeader success render 1`] = class="dtc-block-header" >
标题1
-
, "debug": [Function], diff --git a/src/blockHeader/__tests__/blockHeader.test.tsx b/src/blockHeader/__tests__/blockHeader.test.tsx index f41e04386..6f082db81 100644 --- a/src/blockHeader/__tests__/blockHeader.test.tsx +++ b/src/blockHeader/__tests__/blockHeader.test.tsx @@ -2,28 +2,29 @@ import React from 'react'; import { cleanup, fireEvent, render } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; -import BlockHeader, { SizeType } from '../index'; +import BlockHeader, { IBlockHeaderProps, SizeType } from '../index'; -const props = { +const props: IBlockHeaderProps = { title: '标题1', }; -const props2 = { +const props2: IBlockHeaderProps = { title: '标题2', - beforeTitle: Icon, - afterTitle: '说明文字', + addonBefore: Icon, + description: '说明文字', addonAfter:
新增按钮
, size: 'small' as SizeType, - titleRowClassName: 'test-row-className', - titleClassName: 'test-title-className', + className: 'test__className', + style: { height: '100px' }, + hasBottom: true, }; -const props3 = { +const props3: IBlockHeaderProps = { title: 'hover', tooltip: 'hover 展示', }; -const props4 = { +const props4: IBlockHeaderProps = { title: 'hover', tooltip: 'hover 展示', - afterTitle: '我的优先级更高', + description: '说明文字', }; const prefixCls = 'dtc-block-header'; @@ -42,21 +43,10 @@ describe('test BlockHeader render', () => { expect(getByText('标题2')).toBeTruthy(); }); test('should render BlockHeader props default in BlockHeader', () => { - const { container } = render(); + const { container } = render(); const wrap = container.firstChild; - expect(wrap!.firstChild!.firstChild!.firstChild).toHaveClass(`${prefixCls}-before-title`); - fireEvent.click(document.getElementsByClassName(`${prefixCls}-title-row`)[0]); - }); - test('should render BlockHeader with different props', () => { - const { container, getByText } = render(); - const wrap = container.firstChild; - expect(wrap).toHaveClass(`${prefixCls}`); - expect(wrap!.lastChild).toHaveClass(`${prefixCls}-content`); - expect(wrap!.firstChild).toHaveClass(`test-row-className`); - expect(getByText('标题2')).toHaveClass('test-title-className'); - expect(getByText('说明文字')).toHaveClass(`${prefixCls}-after-title`); - expect(getByText('新增按钮')).toHaveClass(`test-button-after`); - expect(getByText('Icon')).toBeTruthy(); + expect(wrap!.firstChild!.firstChild!.firstChild).toHaveClass('title__addon-before'); + fireEvent.click(document.getElementsByClassName(`${prefixCls}__title`)[0]); }); test('should render BlockHeader test click event', () => { const onChange = jest.fn(); @@ -66,7 +56,7 @@ describe('test BlockHeader render', () => { ); expect(getByText('收起')).toBeTruthy(); - fireEvent.click(document.getElementsByClassName(`${prefixCls}-title-row`)[0]); + fireEvent.click(document.getElementsByClassName(`${prefixCls}__title`)[0]); expect(getByText('展开')).toBeTruthy(); expect(onChange).toHaveBeenCalledTimes(1); }); @@ -77,21 +67,20 @@ describe('test BlockHeader render', () => { ); expect(getByText('收起')).toBeTruthy(); - fireEvent.click(document.getElementsByClassName(`${prefixCls}-title-row`)[0]); + fireEvent.click(document.getElementsByClassName(`${prefixCls}__title`)[0]); expect(getByText('展开')).toBeTruthy(); }); test('should render BlockHeader with different props', () => { const { container, getByText } = render(); const wrap = container.firstChild; - expect(wrap).toHaveClass(`${prefixCls}`); - expect(wrap!.lastChild).toHaveClass(`${prefixCls}-content`); - expect(wrap!.firstChild).toHaveClass(`test-row-className`); - expect(getByText('标题2')).toHaveClass('test-title-className'); - expect(getByText('说明文字')).toHaveClass(`${prefixCls}-after-title`); + expect(wrap).toHaveClass(`${prefixCls} test__className`); + expect(wrap).toHaveStyle({ height: '100px', marginBottom: '16px' }); + expect(getByText('标题2')).toHaveClass('title__text'); + expect(getByText('说明文字')).toHaveClass('title__description'); expect(getByText('Icon')).toBeTruthy(); }); - test('should render BlockHeader className when isSmall is small', () => { - const props = { title: '测试1', showBackground: false }; + test('should render BlockHeader background success', () => { + const props = { title: '测试1', background: false }; const { container } = render(); const wrap = container.firstChild; expect(wrap!.firstChild).not.toHaveClass(`background`); @@ -99,11 +88,9 @@ describe('test BlockHeader render', () => { test('should render BlockHeader className when isSmall is small', () => { const { container, getByText } = render(); const wrap = container.firstChild!; - expect(wrap).toHaveClass(`${prefixCls}`); - expect(wrap.lastChild).toHaveClass(`${prefixCls}-content`); - expect(wrap.firstChild).toHaveClass(`test-row-className`); - expect(getByText('标题2')).toHaveClass('test-title-className'); - expect(getByText('说明文字')).toHaveClass(`${prefixCls}-after-title`); + expect(wrap).toHaveClass(`${prefixCls} test__className`); + expect(getByText('标题2')).toHaveClass('title__text'); + expect(getByText('说明文字')).toHaveClass('title__description'); expect(getByText('Icon')).toBeTruthy(); }); @@ -114,32 +101,32 @@ describe('test BlockHeader render', () => { expect(afterTitleWrap!.firstChild).toHaveClass('anticon-question-circle'); }); - test('should render BlockHeader tooltip and afterTitle success', () => { + test('should render BlockHeader tooltip and desc success', () => { const { container } = render(); const wrap = container.firstChild!; const afterTitleWrap = wrap.firstChild!.firstChild!.lastChild; - expect(afterTitleWrap).toHaveTextContent('我的优先级更高'); + expect(afterTitleWrap).toHaveTextContent('说明文字'); }); test('should render BlockHeader correct dom length', () => { - const { container } = render(); + const { container } = render(); const titleBoxWrap = container.firstChild!.firstChild!.firstChild; expect(titleBoxWrap!.childNodes.length).toEqual(1); const { container: container1 } = render( - + ); const titleBoxWrap1 = container1.firstChild!.firstChild!.firstChild; expect(titleBoxWrap1!.childNodes.length).toEqual(3); }); test('should render BlockHeader correct margin-bottom', () => { - const { container: noStyle } = render(); + const { container: noStyle } = render(); expect(noStyle.querySelector('.dtc-block-header')).not.toHaveAttribute('style'); const { container: defaultBottom } = render( - + ); expect(defaultBottom.querySelector('.dtc-block-header')).toHaveStyle({ marginBottom: 16 }); const { container: customizeBottom } = render( - + ); expect(customizeBottom.querySelector('.dtc-block-header')).toHaveStyle({ marginBottom: 10, diff --git a/src/blockHeader/demos/addonAfter.tsx b/src/blockHeader/demos/addonAfter.tsx new file mode 100644 index 000000000..01f89f594 --- /dev/null +++ b/src/blockHeader/demos/addonAfter.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { Input, Space } from 'antd'; +import { BlockHeader } from 'dt-react-component'; + +export default () => ( + + + } + tooltip={{ title: '这里展示问号提示' }} + /> + +); diff --git a/src/blockHeader/demos/customIcon.tsx b/src/blockHeader/demos/addonBefore.tsx similarity index 79% rename from src/blockHeader/demos/customIcon.tsx rename to src/blockHeader/demos/addonBefore.tsx index acad3fefb..5aa1e62a3 100644 --- a/src/blockHeader/demos/customIcon.tsx +++ b/src/blockHeader/demos/addonBefore.tsx @@ -9,12 +9,12 @@ export default () => {
} + addonBefore={} />
} + addonBefore={} /> ); diff --git a/src/blockHeader/demos/basic.tsx b/src/blockHeader/demos/basic.tsx index 3205e8800..ed0b983f2 100644 --- a/src/blockHeader/demos/basic.tsx +++ b/src/blockHeader/demos/basic.tsx @@ -1,16 +1,52 @@ import React, { useState } from 'react'; -import { Switch } from 'antd'; +import { Radio, Space, Switch } from 'antd'; import { BlockHeader } from 'dt-react-component'; +import { SizeType } from 'dt-react-component/blockHeader'; export default () => { + const [size, setSize] = useState('middle'); const [showBackground, setShowBackground] = useState(true); + const [tooltip, setTooltip] = useState(true); + const [description, setDescription] = useState(true); + return ( - <> - 背景: - +
+ + setSize(e.target.value)}> + Small + Middle + Large + + + + + + +

- - + +
); }; diff --git a/src/blockHeader/demos/extraInfo.tsx b/src/blockHeader/demos/extraInfo.tsx deleted file mode 100644 index a097f6d26..000000000 --- a/src/blockHeader/demos/extraInfo.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import { BlockHeader } from 'dt-react-component'; - -export default () => { - return ( - <> - -
- - - ); -}; diff --git a/src/blockHeader/demos/size.tsx b/src/blockHeader/demos/size.tsx deleted file mode 100644 index f3fd4be83..000000000 --- a/src/blockHeader/demos/size.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React, { useState } from 'react'; -import { Radio } from 'antd'; -import { BlockHeader } from 'dt-react-component'; -import { SizeType } from 'dt-react-component/esm/blockHeader'; - -export default () => { - const [size, setSize] = useState('middle'); - return ( - <> - setSize(e.target.value)}> - Default - Small - -
-
- - - ); -}; diff --git a/src/blockHeader/index.md b/src/blockHeader/index.md index 13bf79aef..19e3b083b 100644 --- a/src/blockHeader/index.md +++ b/src/blockHeader/index.md @@ -16,29 +16,29 @@ demo: ## 示例 -基础使用 -标题尺寸 -带提示信息的标题 -自定义 icon +基础使用 +自定义 icon +带提示信息的标题 展开/收起内容 ## API ### BlockHeader -| 参数 | 说明 | 类型 | 默认值 | -| ----------------- | ----------------------------------------- | --------------------------- | ------- | -| title | 标题 | `string` | - | -| beforeTitle | 标题前的图标,默认是一个色块 | `React.ReactNode` | - | -| afterTitle | 标题后的提示图标或文案 | `React.ReactNode` | - | -| tooltip | 默认展示问号提示(优先级低于 `afterTitle`) | `React.ReactNode` | - | -| isSmall | 大标题、小标题,默认为大标题 | `boolean` | `false` | -| titleRowClassName | 标题一行的样式类名 | `string` | - | -| titleClassName | 标题的样式类名 | `string` | - | -| showBackground | 是否显示背景 | `boolean` | `true` | -| defaultExpand | 是否默认展开内容 | `boolean` | `true` | +| 参数 | 说明 | 类型 | 默认值 | +| ----------------- | ---------------------------------- | --------------------------- | -------- | +| title | 标题 | `string` | - | +| addonBefore | 标题前的图标,默认是一个色块 | `React.ReactNode` | - | +| description | 标题提示文案 | `React.ReactNode` | - | +| tooltip | 默认展示问号提示 | `TooltipProps \| TooltipProps['title']` | - | +| addonAfter | 标题后的内容 | `React.ReactNode` | - | +| size | 小标题、中标题,默认为中标题 | `small \| middle \| large` | `middle` | +| className | 标题一行的样式类名 | `string` | - | +| style | 标题的样式 | `React.CSSProperties` | - | +| background | 是否显示背景 | `boolean` | `true` | | expand | 当前展开状态 | `boolean` | | -| hasBottom | 是否有默认下边距 16px | `boolean` | `false` | -| spaceBottom | 自定义下边距,优先级高于 hasBottom | `number` | `0` | -| children | 展开/收起的内容 | `React.ReactNode` | - | +| defaultExpand | 是否默认展开内容 | `boolean` | `true` | +| hasBottom | 是否有默认下边距 16px | `boolean` | `false` | +| spaceBottom | 自定义下边距,优先级高于 hasBottom | `number` | `0` | +| children | 展开/收起的内容 | `React.ReactNode` | - | | onExpand | 展开/收起时的回调 | `(expand: boolean) => void` | - | diff --git a/src/blockHeader/index.tsx b/src/blockHeader/index.tsx index 81a4ec567..67117aee8 100644 --- a/src/blockHeader/index.tsx +++ b/src/blockHeader/index.tsx @@ -3,9 +3,10 @@ import { QuestionCircleOutlined, UpOutlined } from '@ant-design/icons'; import { Tooltip } from 'antd'; import classNames from 'classnames'; +import { LabelTooltipType, toTooltipProps } from '../utils'; import './style.scss'; -export declare type SizeType = 'small' | 'middle' | undefined; +export declare type SizeType = 'small' | 'middle' | 'large'; function isControlled(props: IBlockHeaderProps) { return props.expand !== undefined; @@ -15,16 +16,17 @@ export interface IBlockHeaderProps { /** 标题 */ title: string; /** 标题前的图标,默认是一个色块 */ - beforeTitle?: ReactNode; - /** 标题后的提示图标或文案 */ - afterTitle?: ReactNode; + addonBefore?: ReactNode; + /** 标题后的提示说明文字 */ + description?: ReactNode; /** 默认展示为问号的tooltip */ - tooltip?: ReactNode; - /** 后缀自定义内容块 */ + tooltip?: LabelTooltipType; + /** 后缀自定义内容块 */ addonAfter?: ReactNode; /** * 小标题 font-size: 12px; line-height: 32px * 中标题 font-size: 14px; line-height: 40px + * 大标题 font-size: 16px; line-height: 40px * 默认 中标题 */ size?: SizeType; @@ -33,11 +35,11 @@ export interface IBlockHeaderProps { /** 自定义 Bottom 值 */ spaceBottom?: number; /** 标题一行的样式类名 */ - titleRowClassName?: string; + className?: string; /** 标题的样式类名 */ - titleClassName?: string; + style?: React.CSSProperties; /** 是否显示背景, 默认 true */ - showBackground?: boolean; + background?: boolean; /** 当前展开状态 */ expand?: boolean; /** 是否默认展开内容, 默认 true */ @@ -47,23 +49,26 @@ export interface IBlockHeaderProps { /** 展开/收起时的回调 */ onExpand?: (expand: boolean) => void; } + +const prefixCls = 'dtc-block-header'; +const preTitleRowCls = `${prefixCls}__title`; + const BlockHeader: React.FC = function (props) { - const prefixCls = 'dtc-block-header'; const { title, - afterTitle = '', - tooltip = '', + description = '', + tooltip, size = 'middle', hasBottom = false, spaceBottom = 0, - titleRowClassName = '', - titleClassName = '', - showBackground = true, + className = '', + style = {}, + background = true, defaultExpand = true, addonAfter, expand, children = '', - beforeTitle =
, + addonBefore =
, onExpand, } = props; @@ -71,14 +76,8 @@ const BlockHeader: React.FC = function (props) { const currentExpand = isControlled(props) ? expand : internalExpand; - const preTitleRowCls = `${prefixCls}-title-row`; + const tooltipProps = toTooltipProps(tooltip); - const questionTooltip = tooltip && ( - - - - ); - const newAfterTitle = afterTitle || questionTooltip; let bottomStyle; if (hasBottom) bottomStyle = { marginBottom: 16 }; if (spaceBottom) bottomStyle = { marginBottom: spaceBottom }; @@ -90,38 +89,48 @@ const BlockHeader: React.FC = function (props) { }; return ( -
+
handleExpand(!currentExpand)} > -
- {beforeTitle ? ( -
{beforeTitle}
- ) : null} -
{title}
- {newAfterTitle ? ( -
{newAfterTitle}
+
+ {addonBefore ?
{addonBefore}
: null} +
{title}
+ {tooltipProps?.title ? ( +
+ + + +
) : null} + {description ?
{description}
: null}
- {addonAfter &&
{addonAfter}
} + {addonAfter &&
{addonAfter}
} {children && ( -
-
{currentExpand ? '收起' : '展开'}
- +
+
{currentExpand ? '收起' : '展开'}
+
)}
- -
{children}
+ {children && ( +
+ {children} +
+ )}
); }; diff --git a/src/blockHeader/style.scss b/src/blockHeader/style.scss index a872a457e..31285a51a 100644 --- a/src/blockHeader/style.scss +++ b/src/blockHeader/style.scss @@ -1,12 +1,20 @@ $card_prefix: "dtc-block-header"; .#{$card_prefix} { - &-title-row { + &__title { border-radius: 4px; display: flex; align-items: center; justify-content: space-between; - &-middle { + &--large { + .#{$card_prefix}-title-box { + line-height: 24px; + .#{$card_prefix}-title { + font-size: 16px; + } + } + } + &--middle { .#{$card_prefix}-title-box { line-height: 22px; .#{$card_prefix}-title { @@ -14,7 +22,7 @@ $card_prefix: "dtc-block-header"; } } } - &-small { + &--small { .#{$card_prefix}-title-box { line-height: 20px; .#{$card_prefix}-title { @@ -22,81 +30,98 @@ $card_prefix: "dtc-block-header"; } } } - &-background { + &--background { padding: 0 12px; background-color: #F9F9FA; - &.#{$card_prefix}-title-row { - &-middle { + &.#{$card_prefix}__title { + &--middle, + &--large { height: 40px; } - &-small { + &--small { height: 32px; } } } - &-pointer { + &--pointer { cursor: pointer; } - - .#{$card_prefix}-title-box { - flex: 1; - display: flex; - align-items: center; - .#{$card_prefix}-before-title { + .title { + &__box { + flex: 1; + display: flex; + align-items: center; + } + &__addon-before { margin-right: 8px; + color: #1D78FF; + &--middle, + &--small { + width: 4px; + height: 16px; + background-color: #1D78FF; + } + &--large { + width: 4px; + height: 20px; + background-color: #1D78FF; + } + } + &__tooltip { + display: flex; + margin-right: 4px; + font-size: 16px; + color: #B1B4C5; + cursor: pointer; } - .#{$card_prefix}-title { + &__text { color: #3D446E; font-weight: 500; margin-right: 4px; } - .#{$card_prefix}-after-title { + &__description { display: flex; align-items: center; color: #8B8FA8; font-size: 12px; - &-icon { - font-size: 16px; - color: #B1B4C5; - } } - } - - .#{$card_prefix}-collapse-box { - color: #8B8FA8; - display: flex; - align-items: center; - cursor: pointer; - user-select: none; - .text { - font-size: 12px; - margin: 0 4px; + &__addon-after { + color: #8B8FA8; } - .icon { - font-size: 16px; - transition: transform 0.4s; - &.down { - transform: rotate(-180deg); - } - &.up { - transform: rotate(0deg); + &__collapse { + color: #8B8FA8; + display: flex; + align-items: center; + cursor: pointer; + user-select: none; + .collapse { + &__text { + font-size: 12px; + margin: 0 4px; + } + &__icon { + font-size: 16px; + transition: transform 0.4s; + &--down { + transform: rotate(-180deg); + } + &--up { + transform: rotate(0deg); + } + } } } } } - &-content { - &.hide { - display: none; - } - } - &__beforeTitle { - &-middle { - width: 4px; - height: 16px; - background-color: #1D78FF; - } - &-small { - @extend .#{$card_prefix}__beforeTitle-middle; + &__content { + opacity: 0; + height: 0; + overflow: hidden; + transition: opacity 0.5s ease, height 0.5s ease; + &--active { + opacity: 1; + padding: 16px 24px; + height: auto; } } } diff --git a/src/copy/demos/basic.tsx b/src/copy/demos/basic.tsx index 31c1ba58b..78aa047a5 100644 --- a/src/copy/demos/basic.tsx +++ b/src/copy/demos/basic.tsx @@ -9,21 +9,17 @@ export default () => { return (
- +

{text}

- +

{text}

- React.ReactNode`} - showBackground={false} - size="small" - /> + React.ReactNode`} background={false} size="small" /> `使用 ()=>React.ReactNode,复制该文本`} />

{text}