From 463ab6a2f583971da7ea52cabbd2fd383a4fffaa Mon Sep 17 00:00:00 2001 From: jiasy1616 <103417037+jiasy1616@users.noreply.github.com> Date: Mon, 26 Aug 2024 17:20:40 +0800 Subject: [PATCH 01/28] =?UTF-8?q?feat(layout):=20=E6=96=B0=E5=A2=9E=20layo?= =?UTF-8?q?ut=20=E7=BB=84=E4=BB=B6=20(#452)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(layout): add layout component * fix(layout): add convertUnit in _utils and use parseTNode * fix(Row): use parseTNode * fix(layout): usePrefixClass instead of useConfig * fix: fix cr --------- Co-authored-by: anlyyao --- site/mobile/mobile.config.js | 5 + site/web/site.config.js | 6 + src/_util/convertUnit.ts | 6 + src/index.ts | 1 + src/layout/Col.tsx | 58 ++++ src/layout/Row.tsx | 46 +++ .../__snapshots__/demo.test.jsx.snap | 320 ++++++++++++++++++ .../__snapshots__/demo.test.tsx.snap | 7 + src/layout/__tests__/demo.test.tsx | 21 ++ src/layout/__tests__/index.test.tsx | 52 +++ src/layout/_example/base.tsx | 35 ++ src/layout/_example/index.tsx | 24 ++ src/layout/_example/offset.tsx | 20 ++ src/layout/_example/style/index.less | 22 ++ src/layout/defaultProps.ts | 17 + src/layout/index.ts | 12 + src/layout/layout.en-US.md | 18 + src/layout/layout.md | 19 ++ src/layout/style/css.js | 1 + src/layout/style/index.js | 1 + src/layout/type.ts | 23 ++ test/snap/__snapshots__/csr.test.jsx.snap | 249 ++++++++++++++ test/snap/__snapshots__/ssr.test.jsx.snap | 6 + 23 files changed, 969 insertions(+) create mode 100644 src/_util/convertUnit.ts create mode 100644 src/layout/Col.tsx create mode 100644 src/layout/Row.tsx create mode 100644 src/layout/__tests__/__snapshots__/demo.test.jsx.snap create mode 100644 src/layout/__tests__/__snapshots__/demo.test.tsx.snap create mode 100644 src/layout/__tests__/demo.test.tsx create mode 100644 src/layout/__tests__/index.test.tsx create mode 100644 src/layout/_example/base.tsx create mode 100644 src/layout/_example/index.tsx create mode 100644 src/layout/_example/offset.tsx create mode 100644 src/layout/_example/style/index.less create mode 100644 src/layout/defaultProps.ts create mode 100644 src/layout/index.ts create mode 100644 src/layout/layout.en-US.md create mode 100644 src/layout/layout.md create mode 100644 src/layout/style/css.js create mode 100644 src/layout/style/index.js create mode 100644 src/layout/type.ts diff --git a/site/mobile/mobile.config.js b/site/mobile/mobile.config.js index f46775a5..d5967f20 100644 --- a/site/mobile/mobile.config.js +++ b/site/mobile/mobile.config.js @@ -15,6 +15,11 @@ export default { name: 'icon', component: () => import('tdesign-mobile-react/icon/_example/index.tsx'), }, + { + title: 'Layout 布局', + name: 'layout', + component: () => import('tdesign-mobile-react/layout/_example/index.tsx'), + }, { title: 'Tabs 选项卡', name: 'tabs', diff --git a/site/web/site.config.js b/site/web/site.config.js index 30bb0e7c..f709bd4a 100644 --- a/site/web/site.config.js +++ b/site/web/site.config.js @@ -46,6 +46,12 @@ export default { path: '/mobile-react/components/icon', component: () => import('tdesign-mobile-react/icon/icon.md'), }, + { + title: 'Layout 布局', + name: 'layout', + path: '/mobile-react/components/layout', + component: () => import('tdesign-mobile-react/layout/layout.md'), + }, { title: 'Link 链接', name: 'link', diff --git a/src/_util/convertUnit.ts b/src/_util/convertUnit.ts new file mode 100644 index 00000000..b0a26671 --- /dev/null +++ b/src/_util/convertUnit.ts @@ -0,0 +1,6 @@ +import isNumber from 'lodash/isNumber'; + +export const convertUnit = (val: string | number | undefined) => { + if (val == null) return 0; + return isNumber(val) ? `${val}px` : val; +}; diff --git a/src/index.ts b/src/index.ts index 5e8bc151..91b4e09e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ export * from './button'; export * from './divider'; export * from './fab'; export * from './progress'; +export * from './layout'; export * from './link'; /** diff --git a/src/layout/Col.tsx b/src/layout/Col.tsx new file mode 100644 index 00000000..860460f6 --- /dev/null +++ b/src/layout/Col.tsx @@ -0,0 +1,58 @@ +import React, { forwardRef, useContext } from 'react'; +import classNames from 'classnames'; + +import { TdColProps } from './type'; +import { colDefaultProps } from './defaultProps'; + +import { StyledProps } from '../common'; + +import { convertUnit } from '../_util/convertUnit'; +import parseTNode from '../_util/parseTNode'; +import useDefaultProps from '../hooks/useDefaultProps'; +import { usePrefixClass } from '../hooks/useClass'; + +import { RowContext, RowProps } from './Row'; + +export interface ColProps extends TdColProps, StyledProps { + children?: React.ReactNode; +} + +const calcColPadding = (gutter: RowProps['gutter']) => { + const styles: React.CSSProperties = {}; + + if (!gutter) { + return styles; + } + const gutterVal = convertUnit(Number(gutter) / 2); + styles.paddingRight = gutterVal; + styles.paddingLeft = gutterVal; + return styles; +}; + +const Col = forwardRef((props, ref) => { + const { offset, span, className, children, style: propStyle } = useDefaultProps(props, colDefaultProps); + + const colClass = usePrefixClass('col'); + + const { gutter } = useContext(RowContext); + + const colClassNames = classNames(colClass, className, { + [`${colClass}--${span}`]: span !== undefined, + [`${colClass}--offset-${offset}`]: parseInt(offset as string, 10) >= 0, + }); + + const colStyle = { + ...calcColPadding(gutter), + ...propStyle, + }; + + return ( +
+ {parseTNode(children)} +
+ ); +}); + +Col.displayName = 'Col'; + +export default Col; diff --git a/src/layout/Row.tsx b/src/layout/Row.tsx new file mode 100644 index 00000000..bc3dde65 --- /dev/null +++ b/src/layout/Row.tsx @@ -0,0 +1,46 @@ +import React, { createContext, forwardRef, useMemo } from 'react'; +import classNames from 'classnames'; + +import type { TdRowProps } from './type'; + +import { convertUnit } from '../_util/convertUnit'; +import parseTNode from '../_util/parseTNode'; +import useDefaultProps from '../hooks/useDefaultProps'; +import { usePrefixClass } from '../hooks/useClass'; +import { StyledProps } from '../common'; + +import { rowDefaultProps } from './defaultProps'; + +export interface RowProps extends TdRowProps, StyledProps { + children?: React.ReactNode; +} + +export const RowContext = createContext<{ gutter: RowProps['gutter'] }>({ gutter: undefined }); + +const Row = forwardRef((props, ref) => { + const { children, className = '', gutter, style } = useDefaultProps(props, rowDefaultProps); + + const colClass = usePrefixClass('row'); + + const computedStyle = useMemo(() => { + const mergedStyle = { ...style }; + if (!gutter) { + return mergedStyle; + } + + const gutterVal = convertUnit(-gutter / 2); + + mergedStyle.marginRight = gutterVal; + mergedStyle.marginLeft = gutterVal; + + return mergedStyle; + }, [gutter, style]); + + return ( +
+ {parseTNode(children)} +
+ ); +}); + +export default Row; diff --git a/src/layout/__tests__/__snapshots__/demo.test.jsx.snap b/src/layout/__tests__/__snapshots__/demo.test.jsx.snap new file mode 100644 index 00000000..9aa9c3bf --- /dev/null +++ b/src/layout/__tests__/__snapshots__/demo.test.jsx.snap @@ -0,0 +1,320 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Col > Col baseVue demo works fine 1`] = ` +
+ +
+ +
+ + col-8 + +
+
+ + col-8 + +
+
+ + col-8 + +
+ +
+
+ +
+ + col-4 + +
+
+ + col-16 col-offset-4 + +
+ +
+
+ +
+ + col-12 col-offset-12 + +
+ +
+ +
+`; + +exports[`Col > Col mobileVue demo works fine 1`] = ` +
+

+ Layout 布局 +

+

+ 以规则的网格阵列来指导和规范页面中的版面布局以及信息分布,提高界面内布局的一致性,节约成本。 +

+
+
+

+ 01 组件类型 +

+

+ 基础用法 +

+
+
+ + +
+ +
+ + col-8 + +
+
+ + col-8 + +
+
+ + col-8 + +
+ +
+
+ +
+ + col-4 + +
+
+ + col-16 col-offset-4 + +
+ +
+
+ +
+ + col-12 col-offset-12 + +
+ +
+ + +
+
+
+
+ +

+ 在列元素之间增加间距 +

+
+
+ +
+ +
+ +
+ col-8 +
+ +
+
+ +
+ col-8 +
+ +
+
+ +
+ col-8 +
+ +
+ +
+ +
+
+
+`; + +exports[`Col > Col offsetVue demo works fine 1`] = ` +
+ +
+ +
+ col-8 +
+ +
+
+ +
+ col-8 +
+ +
+
+ +
+ col-8 +
+ +
+ +
+`; diff --git a/src/layout/__tests__/__snapshots__/demo.test.tsx.snap b/src/layout/__tests__/__snapshots__/demo.test.tsx.snap new file mode 100644 index 00000000..51983172 --- /dev/null +++ b/src/layout/__tests__/__snapshots__/demo.test.tsx.snap @@ -0,0 +1,7 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Col > Col baseDemo demo works fine 1`] = `null`; + +exports[`Col > Col indexDemo demo works fine 1`] = `null`; + +exports[`Col > Col offsetDemo demo works fine 1`] = `null`; diff --git a/src/layout/__tests__/demo.test.tsx b/src/layout/__tests__/demo.test.tsx new file mode 100644 index 00000000..f0a4b04c --- /dev/null +++ b/src/layout/__tests__/demo.test.tsx @@ -0,0 +1,21 @@ +import { describe, expect, render, it } from '@test/utils'; + +import baseDemo from '../_example/base'; +import indexDemo from '../_example/index'; +import offsetDemo from '../_example/offset'; + +const mapper = { + baseDemo, + indexDemo, + offsetDemo, +}; + +describe('Col', () => { + Object.keys(mapper).forEach((demoName) => { + it(`Col ${demoName} demo works fine`, () => { + const { container } = render(mapper[demoName]); + + expect(container.firstChild).toMatchSnapshot(); + }); + }); +}); diff --git a/src/layout/__tests__/index.test.tsx b/src/layout/__tests__/index.test.tsx new file mode 100644 index 00000000..f27697f8 --- /dev/null +++ b/src/layout/__tests__/index.test.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { describe, it, expect, render } from '@test/utils'; +import Row from '../Row'; +import Col from '../Col'; + +const prefix = 't'; +const rowName = `${prefix}-row`; +const colName = `${prefix}-col`; + +const TEXT = 'tdesign-mobile-vue'; + +describe('row', () => { + describe('props', () => { + it(':gutter', async () => { + const { container } = render( + + + , + ); + expect(container.firstChild).toHaveStyle('margin-right: -50px; margin-left: -50px;'); + + expect(container.firstChild).toHaveClass(rowName); + }); + }); +}); + +describe('col', () => { + describe('props', () => { + it(':offset', async () => { + const { container } = render( + + + , + ); + const col = container.querySelector(`.${colName}--2`); + expect(col).not.toBeNull(); + expect(container.firstChild).toHaveClass(rowName); + }); + + it(':span', async () => { + const { container } = render( + + {TEXT} + , + ); + + const col = container.querySelector(`.${colName}--2`); + expect(col).not.toBeNull(); + expect(container.querySelector(`.${colName}--2`).textContent).toBe(TEXT); + }); + }); +}); diff --git a/src/layout/_example/base.tsx b/src/layout/_example/base.tsx new file mode 100644 index 00000000..f2d96c57 --- /dev/null +++ b/src/layout/_example/base.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { Col, Row } from 'tdesign-mobile-react'; + +export default function () { + return ( +
+ + + col-8 + + + col-8 + + + col-8 + + + + + + col-4 + + + col-16 col-offset-4 + + + + + + col-12 col-offset-12 + + +
+ ); +} diff --git a/src/layout/_example/index.tsx b/src/layout/_example/index.tsx new file mode 100644 index 00000000..378f6c28 --- /dev/null +++ b/src/layout/_example/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import TDemoBlock from '../../../site/mobile/components/DemoBlock'; +import TDemoHeader from '../../../site/mobile/components/DemoHeader'; +import './style/index.less'; + +import BaseDemo from './base'; +import OffsetDemo from './offset'; + +export default function Base() { + return ( +
+ + + + + + + +
+ ); +} diff --git a/src/layout/_example/offset.tsx b/src/layout/_example/offset.tsx new file mode 100644 index 00000000..a2f042c8 --- /dev/null +++ b/src/layout/_example/offset.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { Col, Row } from 'tdesign-mobile-react'; + +export default function () { + return ( +
+ + +
col-8
+ + +
col-8
+ + +
col-8
+ +
+
+ ); +} diff --git a/src/layout/_example/style/index.less b/src/layout/_example/style/index.less new file mode 100644 index 00000000..1b185e67 --- /dev/null +++ b/src/layout/_example/style/index.less @@ -0,0 +1,22 @@ +.dark { + background-color: #0052d9; +} + +.light { + background-color: #366ef4; +} + +.dark, +.light { + color: #fff; + font-size: 14px; + line-height: 48px; + margin-bottom: 16px; + text-align: center; +} + +.tdesign-mobile-demo { + // background-color: #fff; + min-height: 100%; + box-sizing: border-box; +} diff --git a/src/layout/defaultProps.ts b/src/layout/defaultProps.ts new file mode 100644 index 00000000..20f1594b --- /dev/null +++ b/src/layout/defaultProps.ts @@ -0,0 +1,17 @@ +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdColProps, TdRowProps } from './type'; + +export const rowDefaultProps: TdRowProps = { + /** 列之间的间距(默认单位px) */ + gutter: 0, +}; + +export const colDefaultProps: TdColProps = { + /** 列的偏移量(默认单位px) */ + offset: 0, + /** 列的宽度(默认单位px) */ + span: 0, +}; diff --git a/src/layout/index.ts b/src/layout/index.ts new file mode 100644 index 00000000..68730641 --- /dev/null +++ b/src/layout/index.ts @@ -0,0 +1,12 @@ +import _Col from './Col'; +import _Row from './Row'; + +import './style'; + +export type { ColProps } from './Col'; +export type { RowProps } from './Row'; + +export * from './type'; + +export const Col = _Col; +export const Row = _Row; diff --git a/src/layout/layout.en-US.md b/src/layout/layout.en-US.md new file mode 100644 index 00000000..26e7f7ae --- /dev/null +++ b/src/layout/layout.en-US.md @@ -0,0 +1,18 @@ +:: BASE_DOC :: + +## API + + +### Row Props + +name | type | default | description | required +-- | -- | -- | -- | -- +gutter | String / Number | - | \- | N + + +### Col Props + +name | type | default | description | required +-- | -- | -- | -- | -- +offset | String / Number | - | \- | N +span | String / Number | - | \- | N diff --git a/src/layout/layout.md b/src/layout/layout.md new file mode 100644 index 00000000..b1e6eeba --- /dev/null +++ b/src/layout/layout.md @@ -0,0 +1,19 @@ +:: BASE_DOC :: + +## API +### Col Props + +名称 | 类型 | 默认值 | 说明 | 必传 +-- | -- | -- | -- | -- +className | String | - | 类名 | N +style | Object | - | 样式,TS 类型:`React.CSSProperties` | N +offset | String / Number | - | 列的偏移量(默认单位px) | N +span | String / Number | - | 列的宽度(默认单位px) | N + +### Row Props + +名称 | 类型 | 默认值 | 说明 | 必传 +-- | -- | -- | -- | -- +className | String | - | 类名 | N +style | Object | - | 样式,TS 类型:`React.CSSProperties` | N +gutter | String / Number | - | 列之间的间距(默认单位px) | N diff --git a/src/layout/style/css.js b/src/layout/style/css.js new file mode 100644 index 00000000..6a9a4b13 --- /dev/null +++ b/src/layout/style/css.js @@ -0,0 +1 @@ +import './index.css'; diff --git a/src/layout/style/index.js b/src/layout/style/index.js new file mode 100644 index 00000000..93977d5d --- /dev/null +++ b/src/layout/style/index.js @@ -0,0 +1 @@ +import '../../_common/style/mobile/components/layout/_index.less'; diff --git a/src/layout/type.ts b/src/layout/type.ts new file mode 100644 index 00000000..1a791d31 --- /dev/null +++ b/src/layout/type.ts @@ -0,0 +1,23 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdColProps { + /** + * 列的偏移量(默认单位px) + */ + offset?: string | number; + /** + * 列的宽度(默认单位px) + */ + span?: string | number; +} + +export interface TdRowProps { + /** + * 列之间的间距(默认单位px) + */ + gutter?: string | number; +} diff --git a/test/snap/__snapshots__/csr.test.jsx.snap b/test/snap/__snapshots__/csr.test.jsx.snap index c1793487..a6cfcae1 100644 --- a/test/snap/__snapshots__/csr.test.jsx.snap +++ b/test/snap/__snapshots__/csr.test.jsx.snap @@ -16310,6 +16310,249 @@ exports[`csr snapshot test > csr test src/input/_example/suffix.tsx 1`] = ` `; +exports[`csr snapshot test > csr test src/layout/_example/base.tsx 1`] = ` +
+
+
+
+ col-8 +
+
+ col-8 +
+
+ col-8 +
+
+
+
+ col-4 +
+
+ col-16 col-offset-4 +
+
+
+
+ col-12 col-offset-12 +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/layout/_example/index.tsx 1`] = ` +
+
+
+

+ Layout 布局 +

+

+ 以规则的网格阵列来指导和规范页面中的版面布局以及信息分布,提高界面内布局的一致性,节约成本。 +

+
+
+
+

+ 01 组件类型 +

+

+ 基础用法 +

+
+
+
+
+
+ col-8 +
+
+ col-8 +
+
+ col-8 +
+
+
+
+ col-4 +
+
+ col-16 col-offset-4 +
+
+
+
+ col-12 col-offset-12 +
+
+
+
+
+
+
+ +

+ 在列元素之间增加间距 +

+
+
+
+
+
+
+ col-8 +
+
+
+
+ col-8 +
+
+
+
+ col-8 +
+
+
+
+
+
+
+
+`; + +exports[`csr snapshot test > csr test src/layout/_example/offset.tsx 1`] = ` +
+
+
+
+
+ col-8 +
+
+
+
+ col-8 +
+
+
+
+ col-8 +
+
+
+
+
+`; + exports[`csr snapshot test > csr test src/link/_example/base.tsx 1`] = `
ssr test src/input/_example/status.tsx 1`] = `"
ssr test src/input/_example/suffix.tsx 1`] = `"
标签文字
标签文字
标签文字
"`; +exports[`ssr snapshot test > ssr test src/layout/_example/base.tsx 1`] = `"
col-8
col-8
col-8
col-4
col-16 col-offset-4
col-12 col-offset-12
"`; + +exports[`ssr snapshot test > ssr test src/layout/_example/index.tsx 1`] = `"

Layout 布局

以规则的网格阵列来指导和规范页面中的版面布局以及信息分布,提高界面内布局的一致性,节约成本。

01 组件类型

基础用法

col-8
col-8
col-8
col-4
col-16 col-offset-4
col-12 col-offset-12

在列元素之间增加间距

col-8
col-8
col-8
"`; + +exports[`ssr snapshot test > ssr test src/layout/_example/offset.tsx 1`] = `"
col-8
col-8
col-8
"`; + exports[`ssr snapshot test > ssr test src/link/_example/base.tsx 1`] = `""`; exports[`ssr snapshot test > ssr test src/link/_example/index.tsx 1`] = `"

Link 链接

当功能使用图标即可表意清楚时,可使用纯图标悬浮按钮,例如:添加、发布。

01 组件类型

基础文字链接

下划线文字链接

前置图标文字链接

后置图标文字链接

03 组件样式

链接尺寸

"`; diff --git a/test/snap/__snapshots__/ssr.test.jsx.snap b/test/snap/__snapshots__/ssr.test.jsx.snap index fd46a3f6..4701822c 100644 --- a/test/snap/__snapshots__/ssr.test.jsx.snap +++ b/test/snap/__snapshots__/ssr.test.jsx.snap @@ -116,6 +116,12 @@ exports[`ssr snapshot test > ssr test src/input/_example/status.tsx 1`] = `"
ssr test src/input/_example/suffix.tsx 1`] = `"
标签文字
标签文字
标签文字
"`; +exports[`ssr snapshot test > ssr test src/layout/_example/base.tsx 1`] = `"
col-8
col-8
col-8
col-4
col-16 col-offset-4
col-12 col-offset-12
"`; + +exports[`ssr snapshot test > ssr test src/layout/_example/index.tsx 1`] = `"

Layout 布局

以规则的网格阵列来指导和规范页面中的版面布局以及信息分布,提高界面内布局的一致性,节约成本。

01 组件类型

基础用法

col-8
col-8
col-8
col-4
col-16 col-offset-4
col-12 col-offset-12

在列元素之间增加间距

col-8
col-8
col-8
"`; + +exports[`ssr snapshot test > ssr test src/layout/_example/offset.tsx 1`] = `"
col-8
col-8
col-8
"`; + exports[`ssr snapshot test > ssr test src/link/_example/base.tsx 1`] = `""`; exports[`ssr snapshot test > ssr test src/link/_example/index.tsx 1`] = `"

Link 链接

当功能使用图标即可表意清楚时,可使用纯图标悬浮按钮,例如:添加、发布。

01 组件类型

基础文字链接

下划线文字链接

前置图标文字链接

后置图标文字链接

03 组件样式

链接尺寸

"`; From 4c5791b84893ecfd6278ff2ddd888dc43222c0f6 Mon Sep 17 00:00:00 2001 From: liweijie0812 <674416404@qq.com> Date: Mon, 26 Aug 2024 20:45:27 +0800 Subject: [PATCH 02/28] docs: update cell.md (#497) --- src/cell/cell.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/cell/cell.md b/src/cell/cell.md index c8881de3..56241d59 100644 --- a/src/cell/cell.md +++ b/src/cell/cell.md @@ -6,16 +6,6 @@ isComponent: true toc: false --- -## 单行单元格 - -::: demo _example/single cell -::: - -## 多行单元格 - -::: demo _example/multiple cell -::: - ## API ### Cell Props From 96dcd6a0cce967e72438cb0ef111f6eabb59d206 Mon Sep 17 00:00:00 2001 From: liweijie0812 <674416404@qq.com> Date: Mon, 26 Aug 2024 20:58:25 +0800 Subject: [PATCH 03/28] feat(Divider): support TNode function (#496) --- src/divider/Divider.tsx | 7 +++---- src/divider/__tests__/divider.test.tsx | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/divider/Divider.tsx b/src/divider/Divider.tsx index 911da7c4..c0e19b13 100644 --- a/src/divider/Divider.tsx +++ b/src/divider/Divider.tsx @@ -6,10 +6,9 @@ import { dividerDefaultProps } from './defaultProps'; import { StyledProps } from '../common'; import useConfig from '../_util/useConfig'; import useDefaultProps from '../hooks/useDefaultProps'; +import parseTNode from '../_util/parseTNode'; -export interface DividerProps extends TdDividerProps, StyledProps { - children?: React.ReactNode; -} +export interface DividerProps extends TdDividerProps, StyledProps {} const Divider: FC = (props) => { const { children, align, dashed, layout, content, style } = useDefaultProps(props, dividerDefaultProps); @@ -28,7 +27,7 @@ const Divider: FC = (props) => { return (
-
{contentNode}
+
{parseTNode(contentNode)}
); }; diff --git a/src/divider/__tests__/divider.test.tsx b/src/divider/__tests__/divider.test.tsx index dd6144a3..c7c52249 100644 --- a/src/divider/__tests__/divider.test.tsx +++ b/src/divider/__tests__/divider.test.tsx @@ -9,11 +9,7 @@ describe('Divider', () => { const testId = 'divider test align'; const aligns: TdDividerProps['align'][] = ['left', 'right', 'center']; const { getByTestId } = render( -
- {aligns?.map((align, index) => ( - - ))} -
, +
{aligns?.map((align, index) => )}
, ); const instance = await waitFor(() => getByTestId(testId)); @@ -28,11 +24,7 @@ describe('Divider', () => { const testId = 'divider test layout'; const layouts: TdDividerProps['layout'][] = ['horizontal', 'vertical']; const { getByTestId } = render( -
- {layouts?.map((layout, index) => ( - - ))} -
, +
{layouts?.map((layout, index) => )}
, ); const instance = await waitFor(() => getByTestId(testId)); @@ -56,6 +48,14 @@ describe('Divider', () => { }); }); + describe('function', () => { + it(':content', async () => { + const content = () => 'DividerContent'; + const { getByText } = render({content}); + expect(getByText(content()).textContent).toBeTruthy(); + }); + }); + describe('slot', () => { it(':children', async () => { const content = 'DividerContent'; From dea8dbc0c430e8cdc906c86a677486c877221073 Mon Sep 17 00:00:00 2001 From: "Y." Date: Tue, 27 Aug 2024 14:36:24 +0800 Subject: [PATCH 04/28] fix(Badge): fix style error when content is ReactNode (#499) * fix(Badge): fix style error when content is ReactNode * test: update csr and ssr snap --- src/badge/Badge.tsx | 14 +- test/snap/__snapshots__/csr.test.jsx.snap | 880 ++++++++++------------ test/snap/__snapshots__/ssr.test.jsx.snap | 18 +- 3 files changed, 423 insertions(+), 489 deletions(-) diff --git a/src/badge/Badge.tsx b/src/badge/Badge.tsx index 3d8909dc..9d876472 100644 --- a/src/badge/Badge.tsx +++ b/src/badge/Badge.tsx @@ -81,12 +81,12 @@ const Badge = forwardRef((originProps, ref) => { const childNode = content || children; - const readerContent = () => - childNode && ( -
- {parseTNode(childNode)} -
- ); + const readerContent = () => { + if (typeof content === 'string') { + return {content}; + } + return parseTNode(childNode); + }; const readerBadge = () => { if (!isShowBadge) return null; @@ -99,7 +99,7 @@ const Badge = forwardRef((originProps, ref) => { return (
- {readerContent()} +
{readerContent()}
{readerBadge()}
); diff --git a/test/snap/__snapshots__/csr.test.jsx.snap b/test/snap/__snapshots__/csr.test.jsx.snap index a6cfcae1..d4c5db5b 100644 --- a/test/snap/__snapshots__/csr.test.jsx.snap +++ b/test/snap/__snapshots__/csr.test.jsx.snap @@ -308,22 +308,18 @@ exports[`csr snapshot test > csr test src/badge/_example/base.tsx 1`] = `
- - - - - + +
csr test src/badge/_example/base.tsx 1`] = `
- - - + + 按钮 + +
csr test src/badge/_example/base.tsx 1`] = `
- - - - - + +
csr test src/badge/_example/base.tsx 1`] = `
- - - + + 按钮 + +
csr test src/badge/_example/base.tsx 1`] = `
- - - + + +
csr test src/badge/_example/index.tsx 1`] = `
- - - - - + +
csr test src/badge/_example/index.tsx 1`] = `
- - - + + 按钮 + +
csr test src/badge/_example/index.tsx 1`] = `
- - - - - + +
csr test src/badge/_example/index.tsx 1`] = `
- - - + + 按钮 + +
csr test src/badge/_example/index.tsx 1`] = `
- - - + + +
csr test src/badge/_example/index.tsx 1`] = `
- - - - - + +
csr test src/badge/_example/index.tsx 1`] = `
- - - - - + +
csr test src/badge/_example/index.tsx 1`] = `
- - - + + +
csr test src/badge/_example/index.tsx 1`] = `
+
csr test src/badge/_example/index.tsx 1`] = `
+
csr test src/badge/_example/index.tsx 1`] = `
+
csr test src/badge/_example/size.tsx 1`] = `
+
csr test src/badge/_example/size.tsx 1`] = `
+
csr test src/badge/_example/theme.tsx 1`] = `
- - - - - + +
csr test src/badge/_example/theme.tsx 1`] = `
- - - - - + +
csr test src/badge/_example/theme.tsx 1`] = `
- - - + + +
csr test src/badge/_example/theme.tsx 1`] = `
+
csr test src/cell/_example/base.tsx 1`] = `
+
@@ -3194,6 +3151,9 @@ exports[`csr snapshot test > csr test src/cell/_example/base.tsx 1`] = `
+
@@ -3913,6 +3873,9 @@ exports[`csr snapshot test > csr test src/cell/_example/multiple.tsx 1`] = `
+
@@ -4280,6 +4243,9 @@ exports[`csr snapshot test > csr test src/cell/_example/single.tsx 1`] = `
+
@@ -6488,44 +6454,40 @@ exports[`csr snapshot test > csr test src/grid/_example/badge.tsx 1`] = `
-
-
-
-
-
+ class="t-loading__dot" + style="animation-duration: 0.8s; animation-delay: 0s;" + /> +
+
-
- + +
csr test src/grid/_example/badge.tsx 1`] = `
-
-
-
-
-
+ class="t-loading__dot" + style="animation-duration: 0.8s; animation-delay: 0s;" + /> +
+
-
- + +
csr test src/grid/_example/badge.tsx 1`] = `
-
-
-
-
-
+ class="t-loading__dot" + style="animation-duration: 0.8s; animation-delay: 0s;" + /> +
+
-
- + +
csr test src/grid/_example/badge.tsx 1`] = `
-
-
-
-
-
+ class="t-loading__dot" + style="animation-duration: 0.8s; animation-delay: 0s;" + /> +
+
-
- + +
csr test src/grid/_example/index.tsx 1`] = `
-
-
-
-
-
+ class="t-loading__dot" + style="animation-duration: 0.8s; animation-delay: 0s;" + /> +
+
-
- + +
csr test src/grid/_example/index.tsx 1`] = `
-
-
-
-
-
+ class="t-loading__dot" + style="animation-duration: 0.8s; animation-delay: 0s;" + /> +
+
-
- + +
csr test src/grid/_example/index.tsx 1`] = `
-
-
-
-
-
+ class="t-loading__dot" + style="animation-duration: 0.8s; animation-delay: 0s;" + /> +
+
-
- + +
csr test src/grid/_example/index.tsx 1`] = `
-
-
-
-
-
+ class="t-loading__dot" + style="animation-duration: 0.8s; animation-delay: 0s;" + /> +
+
-
- + +
ssr test src/back-top/_example/base.tsx 1`] = `" ssr test src/back-top/_example/index.tsx 1`] = `"

BackTop 返回顶部

当页面过长往下滑动是会出现返回顶部的便捷操作,帮助用户快速回到页面顶部

形状

"`; -exports[`ssr snapshot test > ssr test src/badge/_example/base.tsx 1`] = `"
红点徽标
消息
数字徽标
消息
8
2
8
自定义徽标
NEW
"`; +exports[`ssr snapshot test > ssr test src/badge/_example/base.tsx 1`] = `"
红点徽标
消息
数字徽标
消息
8
2
8
自定义徽标
NEW
"`; -exports[`ssr snapshot test > ssr test src/badge/_example/index.tsx 1`] = `"

Badge 徽标

用于告知用户,该区域的状态变化或者待处理任务的数量。

01 组件类型

红点徽标
消息
数字徽标
消息
8
2
8
自定义徽标
NEW

02 组件样式

圆形徽标
2
方形徽标
2
气泡徽标
领积分
角标
单行标题
New

03 组件尺寸

Large
8
Middle
8
"`; +exports[`ssr snapshot test > ssr test src/badge/_example/index.tsx 1`] = `"

Badge 徽标

用于告知用户,该区域的状态变化或者待处理任务的数量。

01 组件类型

红点徽标
消息
数字徽标
消息
8
2
8
自定义徽标
NEW

02 组件样式

圆形徽标
2
方形徽标
2
气泡徽标
领积分
角标
单行标题
New

03 组件尺寸

Large
8
Middle
8
"`; -exports[`ssr snapshot test > ssr test src/badge/_example/size.tsx 1`] = `"
Large
8
Middle
8
"`; +exports[`ssr snapshot test > ssr test src/badge/_example/size.tsx 1`] = `"
Large
8
Middle
8
"`; -exports[`ssr snapshot test > ssr test src/badge/_example/theme.tsx 1`] = `"
圆形徽标
2
方形徽标
2
气泡徽标
领积分
角标
单行标题
New
"`; +exports[`ssr snapshot test > ssr test src/badge/_example/theme.tsx 1`] = `"
圆形徽标
2
方形徽标
2
气泡徽标
领积分
角标
单行标题
New
"`; exports[`ssr snapshot test > ssr test src/button/_example/base.tsx 1`] = `"
"`; @@ -25120,13 +25054,13 @@ exports[`ssr snapshot test > ssr test src/button/_example/test.tsx 1`] = `"
exports[`ssr snapshot test > ssr test src/button/_example/theme.tsx 1`] = `"
"`; -exports[`ssr snapshot test > ssr test src/cell/_example/base.tsx 1`] = `"

Cell 单元格

一行内容/功能的垂直排列方式。一行项目左侧为主要内容展示区域,右侧可增加更多操作内容

01 类型

单行单元格

单行标题
单行标题 *
单行标题
16
单行标题
单行标题
辅助信息
单行标题

02

多行单元格

单行标题
一段很长很长的内容文字
单行标题 *
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
16
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
辅助信息
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行高度不定,长文本自动换行,该选项的描述是一段很长的内容
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行带头像
一段很长很长的内容文字
多行带图片
一段很长很长的内容文字

03 组件样式

卡片单元格

单行标题
单行标题
单行标题
"`; +exports[`ssr snapshot test > ssr test src/cell/_example/base.tsx 1`] = `"

Cell 单元格

一行内容/功能的垂直排列方式。一行项目左侧为主要内容展示区域,右侧可增加更多操作内容

01 类型

单行单元格

单行标题
单行标题 *
单行标题
16
单行标题
单行标题
辅助信息
单行标题

02

多行单元格

单行标题
一段很长很长的内容文字
单行标题 *
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
16
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
辅助信息
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行高度不定,长文本自动换行,该选项的描述是一段很长的内容
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行带头像
一段很长很长的内容文字
多行带图片
一段很长很长的内容文字

03 组件样式

卡片单元格

单行标题
单行标题
单行标题
"`; exports[`ssr snapshot test > ssr test src/cell/_example/group.tsx 1`] = `"
单行标题
单行标题
单行标题
"`; -exports[`ssr snapshot test > ssr test src/cell/_example/multiple.tsx 1`] = `"
单行标题
一段很长很长的内容文字
单行标题 *
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
16
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
辅助信息
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行高度不定,长文本自动换行,该选项的描述是一段很长的内容
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行带头像
一段很长很长的内容文字
多行带图片
一段很长很长的内容文字
"`; +exports[`ssr snapshot test > ssr test src/cell/_example/multiple.tsx 1`] = `"
单行标题
一段很长很长的内容文字
单行标题 *
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
16
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
辅助信息
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行高度不定,长文本自动换行,该选项的描述是一段很长的内容
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行带头像
一段很长很长的内容文字
多行带图片
一段很长很长的内容文字
"`; -exports[`ssr snapshot test > ssr test src/cell/_example/single.tsx 1`] = `"
单行标题
单行标题 *
单行标题
16
单行标题
单行标题
辅助信息
单行标题
"`; +exports[`ssr snapshot test > ssr test src/cell/_example/single.tsx 1`] = `"
单行标题
单行标题 *
单行标题
16
单行标题
单行标题
辅助信息
单行标题
"`; exports[`ssr snapshot test > ssr test src/count-down/_example/base.tsx 1`] = `"
时分秒
带毫秒
带方形底
带圆形底
带单位
无底色带单位
"`; @@ -25140,7 +25074,7 @@ exports[`ssr snapshot test > ssr test src/divider/_example/index.tsx 1`] = `" ssr test src/divider/_example/theme.tsx 1`] = `"
虚线样式
"`; -exports[`ssr snapshot test > ssr test src/grid/_example/badge.tsx 1`] = `"
标题文字
8
标题文字
13
标题五字内
NEW
标题五字内
"`; +exports[`ssr snapshot test > ssr test src/grid/_example/badge.tsx 1`] = `"
标题文字
8
标题文字
13
标题五字内
NEW
标题五字内
"`; exports[`ssr snapshot test > ssr test src/grid/_example/base.tsx 1`] = `"
标题文字
标题文字
标题文字
标题文字
最多四个字
标题文字
标题文字
标题文字
最多五个字
标题文字
标题文字
最多六个文字
"`; @@ -25152,7 +25086,7 @@ exports[`ssr snapshot test > ssr test src/grid/_example/desc.tsx 1`] = `"
ssr test src/grid/_example/icon.tsx 1`] = `"
分享
收藏
保存
编辑
"`; -exports[`ssr snapshot test > ssr test src/grid/_example/index.tsx 1`] = `"

Grid 宫格

用于功能入口布局,将页面或特定区域切分成若干等大的区块,形成若干功能入口。

01 组件类型

基础宫格

标题文字
标题文字
标题文字
标题文字
最多四个字
标题文字
标题文字
标题文字
最多五个字
标题文字
标题文字
最多六个文字

带描述宫格

标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字

带边框宫格

标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字

带徽标宫格

标题文字
8
标题文字
13
标题五字内
NEW
标题五字内

可滑动宫格

标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字

02 组件样式

可传图标的宫格

分享
收藏
保存
编辑

卡片宫格

标题文字
描述文字
标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字
"`; +exports[`ssr snapshot test > ssr test src/grid/_example/index.tsx 1`] = `"

Grid 宫格

用于功能入口布局,将页面或特定区域切分成若干等大的区块,形成若干功能入口。

01 组件类型

基础宫格

标题文字
标题文字
标题文字
标题文字
最多四个字
标题文字
标题文字
标题文字
最多五个字
标题文字
标题文字
最多六个文字

带描述宫格

标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字

带边框宫格

标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字

带徽标宫格

标题文字
8
标题文字
13
标题五字内
NEW
标题五字内

可滑动宫格

标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字

02 组件样式

可传图标的宫格

分享
收藏
保存
编辑

卡片宫格

标题文字
描述文字
标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字
"`; exports[`ssr snapshot test > ssr test src/grid/_example/scroll.tsx 1`] = `"
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
"`; diff --git a/test/snap/__snapshots__/ssr.test.jsx.snap b/test/snap/__snapshots__/ssr.test.jsx.snap index 4701822c..a9fa0e2d 100644 --- a/test/snap/__snapshots__/ssr.test.jsx.snap +++ b/test/snap/__snapshots__/ssr.test.jsx.snap @@ -4,13 +4,13 @@ exports[`ssr snapshot test > ssr test src/back-top/_example/base.tsx 1`] = `" ssr test src/back-top/_example/index.tsx 1`] = `"

BackTop 返回顶部

当页面过长往下滑动是会出现返回顶部的便捷操作,帮助用户快速回到页面顶部

形状

"`; -exports[`ssr snapshot test > ssr test src/badge/_example/base.tsx 1`] = `"
红点徽标
消息
数字徽标
消息
8
2
8
自定义徽标
NEW
"`; +exports[`ssr snapshot test > ssr test src/badge/_example/base.tsx 1`] = `"
红点徽标
消息
数字徽标
消息
8
2
8
自定义徽标
NEW
"`; -exports[`ssr snapshot test > ssr test src/badge/_example/index.tsx 1`] = `"

Badge 徽标

用于告知用户,该区域的状态变化或者待处理任务的数量。

01 组件类型

红点徽标
消息
数字徽标
消息
8
2
8
自定义徽标
NEW

02 组件样式

圆形徽标
2
方形徽标
2
气泡徽标
领积分
角标
单行标题
New

03 组件尺寸

Large
8
Middle
8
"`; +exports[`ssr snapshot test > ssr test src/badge/_example/index.tsx 1`] = `"

Badge 徽标

用于告知用户,该区域的状态变化或者待处理任务的数量。

01 组件类型

红点徽标
消息
数字徽标
消息
8
2
8
自定义徽标
NEW

02 组件样式

圆形徽标
2
方形徽标
2
气泡徽标
领积分
角标
单行标题
New

03 组件尺寸

Large
8
Middle
8
"`; -exports[`ssr snapshot test > ssr test src/badge/_example/size.tsx 1`] = `"
Large
8
Middle
8
"`; +exports[`ssr snapshot test > ssr test src/badge/_example/size.tsx 1`] = `"
Large
8
Middle
8
"`; -exports[`ssr snapshot test > ssr test src/badge/_example/theme.tsx 1`] = `"
圆形徽标
2
方形徽标
2
气泡徽标
领积分
角标
单行标题
New
"`; +exports[`ssr snapshot test > ssr test src/badge/_example/theme.tsx 1`] = `"
圆形徽标
2
方形徽标
2
气泡徽标
领积分
角标
单行标题
New
"`; exports[`ssr snapshot test > ssr test src/button/_example/base.tsx 1`] = `"
"`; @@ -34,13 +34,13 @@ exports[`ssr snapshot test > ssr test src/button/_example/test.tsx 1`] = `"
exports[`ssr snapshot test > ssr test src/button/_example/theme.tsx 1`] = `"
"`; -exports[`ssr snapshot test > ssr test src/cell/_example/base.tsx 1`] = `"

Cell 单元格

一行内容/功能的垂直排列方式。一行项目左侧为主要内容展示区域,右侧可增加更多操作内容

01 类型

单行单元格

单行标题
单行标题 *
单行标题
16
单行标题
单行标题
辅助信息
单行标题

02

多行单元格

单行标题
一段很长很长的内容文字
单行标题 *
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
16
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
辅助信息
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行高度不定,长文本自动换行,该选项的描述是一段很长的内容
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行带头像
一段很长很长的内容文字
多行带图片
一段很长很长的内容文字

03 组件样式

卡片单元格

单行标题
单行标题
单行标题
"`; +exports[`ssr snapshot test > ssr test src/cell/_example/base.tsx 1`] = `"

Cell 单元格

一行内容/功能的垂直排列方式。一行项目左侧为主要内容展示区域,右侧可增加更多操作内容

01 类型

单行单元格

单行标题
单行标题 *
单行标题
16
单行标题
单行标题
辅助信息
单行标题

02

多行单元格

单行标题
一段很长很长的内容文字
单行标题 *
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
16
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
辅助信息
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行高度不定,长文本自动换行,该选项的描述是一段很长的内容
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行带头像
一段很长很长的内容文字
多行带图片
一段很长很长的内容文字

03 组件样式

卡片单元格

单行标题
单行标题
单行标题
"`; exports[`ssr snapshot test > ssr test src/cell/_example/group.tsx 1`] = `"
单行标题
单行标题
单行标题
"`; -exports[`ssr snapshot test > ssr test src/cell/_example/multiple.tsx 1`] = `"
单行标题
一段很长很长的内容文字
单行标题 *
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
16
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
辅助信息
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行高度不定,长文本自动换行,该选项的描述是一段很长的内容
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行带头像
一段很长很长的内容文字
多行带图片
一段很长很长的内容文字
"`; +exports[`ssr snapshot test > ssr test src/cell/_example/multiple.tsx 1`] = `"
单行标题
一段很长很长的内容文字
单行标题 *
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
16
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字
辅助信息
单行标题
一段很长很长的内容文字
单行标题
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行高度不定,长文本自动换行,该选项的描述是一段很长的内容
一段很长很长的内容文字,长文本自动换行,该选项的描述是一段很长的内容
多行带头像
一段很长很长的内容文字
多行带图片
一段很长很长的内容文字
"`; -exports[`ssr snapshot test > ssr test src/cell/_example/single.tsx 1`] = `"
单行标题
单行标题 *
单行标题
16
单行标题
单行标题
辅助信息
单行标题
"`; +exports[`ssr snapshot test > ssr test src/cell/_example/single.tsx 1`] = `"
单行标题
单行标题 *
单行标题
16
单行标题
单行标题
辅助信息
单行标题
"`; exports[`ssr snapshot test > ssr test src/count-down/_example/base.tsx 1`] = `"
时分秒
带毫秒
带方形底
带圆形底
带单位
无底色带单位
"`; @@ -54,7 +54,7 @@ exports[`ssr snapshot test > ssr test src/divider/_example/index.tsx 1`] = `" ssr test src/divider/_example/theme.tsx 1`] = `"
虚线样式
"`; -exports[`ssr snapshot test > ssr test src/grid/_example/badge.tsx 1`] = `"
标题文字
8
标题文字
13
标题五字内
NEW
标题五字内
"`; +exports[`ssr snapshot test > ssr test src/grid/_example/badge.tsx 1`] = `"
标题文字
8
标题文字
13
标题五字内
NEW
标题五字内
"`; exports[`ssr snapshot test > ssr test src/grid/_example/base.tsx 1`] = `"
标题文字
标题文字
标题文字
标题文字
最多四个字
标题文字
标题文字
标题文字
最多五个字
标题文字
标题文字
最多六个文字
"`; @@ -66,7 +66,7 @@ exports[`ssr snapshot test > ssr test src/grid/_example/desc.tsx 1`] = `"
ssr test src/grid/_example/icon.tsx 1`] = `"
分享
收藏
保存
编辑
"`; -exports[`ssr snapshot test > ssr test src/grid/_example/index.tsx 1`] = `"

Grid 宫格

用于功能入口布局,将页面或特定区域切分成若干等大的区块,形成若干功能入口。

01 组件类型

基础宫格

标题文字
标题文字
标题文字
标题文字
最多四个字
标题文字
标题文字
标题文字
最多五个字
标题文字
标题文字
最多六个文字

带描述宫格

标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字

带边框宫格

标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字

带徽标宫格

标题文字
8
标题文字
13
标题五字内
NEW
标题五字内

可滑动宫格

标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字

02 组件样式

可传图标的宫格

分享
收藏
保存
编辑

卡片宫格

标题文字
描述文字
标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字
"`; +exports[`ssr snapshot test > ssr test src/grid/_example/index.tsx 1`] = `"

Grid 宫格

用于功能入口布局,将页面或特定区域切分成若干等大的区块,形成若干功能入口。

01 组件类型

基础宫格

标题文字
标题文字
标题文字
标题文字
最多四个字
标题文字
标题文字
标题文字
最多五个字
标题文字
标题文字
最多六个文字

带描述宫格

标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字

带边框宫格

标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字

带徽标宫格

标题文字
8
标题文字
13
标题五字内
NEW
标题五字内

可滑动宫格

标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字

02 组件样式

可传图标的宫格

分享
收藏
保存
编辑

卡片宫格

标题文字
描述文字
标题文字
描述文字
标题文字
描述文字
最多六个文字
描述最多六字
标题文字
描述文字
标题文字
描述文字
"`; exports[`ssr snapshot test > ssr test src/grid/_example/scroll.tsx 1`] = `"
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
标题文字
"`; From 3acb9ef4ff53f2e2b840c5d30ae38d3494635433 Mon Sep 17 00:00:00 2001 From: liweijie0812 <674416404@qq.com> Date: Tue, 27 Aug 2024 14:37:26 +0800 Subject: [PATCH 05/28] test(divider): add test case (#498) * test(divider): add test case * test(divider): add test case --- .../vitest-divider.test.jsx.snap | 110 ++++++++++++++++++ src/divider/__tests__/vitest-divider.test.jsx | 55 +++++++++ src/divider/divider.md | 4 +- tsconfig.json | 3 +- 4 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 src/divider/__tests__/__snapshots__/vitest-divider.test.jsx.snap create mode 100644 src/divider/__tests__/vitest-divider.test.jsx diff --git a/src/divider/__tests__/__snapshots__/vitest-divider.test.jsx.snap b/src/divider/__tests__/__snapshots__/vitest-divider.test.jsx.snap new file mode 100644 index 00000000..e472676a --- /dev/null +++ b/src/divider/__tests__/__snapshots__/vitest-divider.test.jsx.snap @@ -0,0 +1,110 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Divider Component > props.align is equal to center 1`] = ` +
+ +
+`; + +exports[`Divider Component > props.align is equal to left 1`] = ` +
+ +
+`; + +exports[`Divider Component > props.align is equal to right 1`] = ` +
+ +
+`; + +exports[`Divider Component > props.children works fine 1`] = ` +
+ +
+`; + +exports[`Divider Component > props.content works fine 1`] = ` +
+ +
+`; + +exports[`Divider Component > props.layout is equal to horizontal 1`] = ` +
+ +`; + +exports[`Divider Component > props.layout is equal to vertical 1`] = ` +
+ +`; diff --git a/src/divider/__tests__/vitest-divider.test.jsx b/src/divider/__tests__/vitest-divider.test.jsx new file mode 100644 index 00000000..95566cbd --- /dev/null +++ b/src/divider/__tests__/vitest-divider.test.jsx @@ -0,0 +1,55 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/** + * 该文件由脚本自动生成,如需修改请联系 PMC + * This file generated by scripts of tdesign-api. `npm run api:docs Divider React(Mobile) vitest,finalProject` + * If you need to modify this file, contact PMC first please. + */ +import React from 'react'; +import { render } from '@test/utils'; +import { Divider } from '..'; + +describe('Divider Component', () => { + ['left', 'right', 'center'].forEach((item) => { + it(`props.align is equal to ${item}`, () => { + const { container } = render(Text); + expect(container.firstChild).toHaveClass(`t-divider--${item}`); + expect(container).toMatchSnapshot(); + }); + }); + + it('props.children works fine', () => { + const { container } = render( + + TNode + , + ); + expect(container.querySelector('.custom-node')).toBeTruthy(); + expect(container).toMatchSnapshot(); + }); + + it('props.content works fine', () => { + const { container } = render(TNode}>); + expect(container.querySelector('.custom-node')).toBeTruthy(); + expect(container).toMatchSnapshot(); + }); + + it('props.dashed works fine', () => { + // dashed default value is false + const { container: container1 } = render(); + expect(container1.querySelector(`.${'t-divider--dashed'}`)).toBeFalsy(); + // dashed = true + const { container: container2 } = render(); + expect(container2.firstChild).toHaveClass('t-divider--dashed'); + // dashed = false + const { container: container3 } = render(); + expect(container3.querySelector(`.${'t-divider--dashed'}`)).toBeFalsy(); + }); + + ['horizontal', 'vertical'].forEach((item) => { + it(`props.layout is equal to ${item}`, () => { + const { container } = render(); + expect(container.firstChild).toHaveClass(`t-divider--${item}`); + expect(container).toMatchSnapshot(); + }); + }); +}); diff --git a/src/divider/divider.md b/src/divider/divider.md index 87e28ad5..392f272c 100644 --- a/src/divider/divider.md +++ b/src/divider/divider.md @@ -8,7 +8,7 @@ className | String | - | 类名 | N style | Object | - | 样式,TS 类型:`React.CSSProperties` | N align | String | center | 文本位置(仅在水平分割线有效)。可选项:left/right/center | N -children | TNode | - | 子元素,同 content。TS 类型:`string | TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N -content | TNode | - | 子元素。TS 类型:`string | TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +children | TNode | - | 子元素,同 content。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +content | TNode | - | 子元素。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N dashed | Boolean | false | 是否虚线(仅在水平分割线有效) | N layout | String | horizontal | 分隔线类型有两种:水平和垂直。可选项:horizontal/vertical | N diff --git a/tsconfig.json b/tsconfig.json index 87c3593c..de500ac4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -32,7 +32,8 @@ "cjs", "esm", "es", - "site" + "site", + "**/*.jsx", ], "compileOnSave": false } From b346021c17506ca175ef5c9d6b0d5ecbd1ecf0c9 Mon Sep 17 00:00:00 2001 From: slatejack <39983389+slatejack@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:41:52 +0800 Subject: [PATCH 06/28] =?UTF-8?q?feat(NoticeBar):=20NoticeBar=20=E5=AF=B9?= =?UTF-8?q?=E9=BD=90=20mobile-vue=20(#501)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(notice-bar): update notice bar update notice bar style to v2, implementation of synchronized vue feat #404 * fix(notice-bar): fix notice-bar demo entry path, fix notice-bar render prefixIcon method * feat(notice-bar): update notice-bar demo * style(notice-bar): fix spell check * fix(notice-bar): fix with code review use useClassPrefix instead useContext, clear timer and optimize assignment --- site/mobile/mobile.config.js | 2 +- src/notice-bar/NoticeBar.tsx | 283 ++--- src/notice-bar/_example/base.tsx | 6 + src/notice-bar/_example/controller.jsx | 30 - src/notice-bar/_example/custom.tsx | 16 + src/notice-bar/_example/customization.tsx | 19 + src/notice-bar/_example/event.jsx | 34 - src/notice-bar/_example/event.tsx | 19 + src/notice-bar/_example/icon.jsx | 13 - src/notice-bar/_example/icon.tsx | 6 + src/notice-bar/_example/mobile.jsx | 82 -- src/notice-bar/_example/mobile.tsx | 48 + src/notice-bar/_example/multi-line.jsx | 43 - src/notice-bar/_example/scrolling.jsx | 21 - src/notice-bar/_example/scrolling.tsx | 17 + src/notice-bar/_example/static.jsx | 15 - src/notice-bar/_example/style/index.less | 23 + src/notice-bar/_example/suffixIcon.tsx | 7 + src/notice-bar/_example/theme.jsx | 16 - src/notice-bar/_example/theme.tsx | 14 + src/notice-bar/defaultProps.ts | 7 +- src/notice-bar/notice-bar.en-US.md | 22 + src/notice-bar/notice-bar.md | 16 +- src/notice-bar/style/index.js | 2 +- src/notice-bar/type.ts | 23 +- test/snap/__snapshots__/csr.test.jsx.snap | 1230 +++++++++++++++++++++ test/snap/__snapshots__/ssr.test.jsx.snap | 18 + 27 files changed, 1618 insertions(+), 414 deletions(-) create mode 100644 src/notice-bar/_example/base.tsx delete mode 100644 src/notice-bar/_example/controller.jsx create mode 100644 src/notice-bar/_example/custom.tsx create mode 100644 src/notice-bar/_example/customization.tsx delete mode 100644 src/notice-bar/_example/event.jsx create mode 100644 src/notice-bar/_example/event.tsx delete mode 100644 src/notice-bar/_example/icon.jsx create mode 100644 src/notice-bar/_example/icon.tsx delete mode 100644 src/notice-bar/_example/mobile.jsx create mode 100644 src/notice-bar/_example/mobile.tsx delete mode 100644 src/notice-bar/_example/multi-line.jsx delete mode 100644 src/notice-bar/_example/scrolling.jsx create mode 100644 src/notice-bar/_example/scrolling.tsx delete mode 100644 src/notice-bar/_example/static.jsx create mode 100644 src/notice-bar/_example/style/index.less create mode 100644 src/notice-bar/_example/suffixIcon.tsx delete mode 100644 src/notice-bar/_example/theme.jsx create mode 100644 src/notice-bar/_example/theme.tsx create mode 100644 src/notice-bar/notice-bar.en-US.md diff --git a/site/mobile/mobile.config.js b/site/mobile/mobile.config.js index d5967f20..5268ecd2 100644 --- a/site/mobile/mobile.config.js +++ b/site/mobile/mobile.config.js @@ -220,7 +220,7 @@ export default { { title: 'NoticeBar 公告栏', name: 'notice-bar', - component: () => import('tdesign-mobile-react/notice-bar/_example/mobile.jsx'), + component: () => import('tdesign-mobile-react/notice-bar/_example/mobile.tsx'), }, { title: 'Result 结果', diff --git a/src/notice-bar/NoticeBar.tsx b/src/notice-bar/NoticeBar.tsx index 0db0c021..6f35814c 100644 --- a/src/notice-bar/NoticeBar.tsx +++ b/src/notice-bar/NoticeBar.tsx @@ -1,10 +1,18 @@ -import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; -import { InfoCircleFilledIcon, CheckCircleFilledIcon, CloseCircleFilledIcon } from 'tdesign-icons-react'; -import cls from 'classnames'; -import { ConfigContext } from '../config-provider'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { InfoCircleFilledIcon, CheckCircleFilledIcon } from 'tdesign-icons-react'; +import classNames from 'classnames'; +import isObject from 'lodash/isObject'; +import parseTNode from 'tdesign-mobile-react/_util/parseTNode'; +import isArray from 'lodash/isArray'; +import Swiper from 'tdesign-mobile-react/swiper'; +import SwiperItem from 'tdesign-mobile-react/swiper/SwiperItem'; +import { usePrefixClass } from 'tdesign-mobile-react/hooks/useClass'; import type { StyledProps } from '../common'; -import type { TdNoticeBarProps, NoticeBarTrigger } from './type'; +import type { TdNoticeBarProps, NoticeBarTrigger, NoticeBarMarquee } from './type'; import useDefault from '../_util/useDefault'; +import useDefaultProps from '../hooks/useDefaultProps'; +import { noticeBarDefaultProps } from './defaultProps'; +import noop from '../_util/noop'; export interface NoticeBarProps extends TdNoticeBarProps, StyledProps {} @@ -44,26 +52,15 @@ const defaultIcons: Record = { info: , success: , warning: , - error: , + error: , }; -function filterUndefinedValue>(obj: T): Partial { - const keys = Object.keys(obj); - const result = keys.reduce((prev, next: keyof T) => { - if (typeof obj[next] !== 'undefined') { - return { - ...prev, - [next]: obj[next], - }; - } - return prev; - }, {}); - - return result; -} - function useAnimationSettingValue() { - const animationSettingValue = useRef(defaultReduceState()); + const animationSettingValue = useRef(null); + if (!animationSettingValue.current) { + // 仅为null时进行初始化 + animationSettingValue.current = defaultReduceState(); + } const [, setState] = useState(0); function updateScroll(obj: Partial) { @@ -89,6 +86,7 @@ function useAnimationSettingValue() { animationSettingValue.current = obj || defaultReduceState(); setState(Math.random()); } + return { animationSettingValue, updateScroll, @@ -98,60 +96,51 @@ function useAnimationSettingValue() { } const NoticeBar: React.FC = (props) => { - const { classPrefix } = useContext(ConfigContext); const { content, - extra, + direction, marquee, + operation, prefixIcon, suffixIcon, theme = 'info', visible, defaultVisible, - onChange, onClick, - } = props; + } = useDefaultProps(props, noticeBarDefaultProps); - const { animationSettingValue, updateScroll, updateAnimationFrame } = useAnimationSettingValue(); - - const name = `${classPrefix}-notice-bar`; + const listDOM = useRef(null); + const itemDOM = useRef(null); + const hasBeenExecute = useRef(false); - const showExtraText = !!extra; - const rootClasses = useMemo(() => cls([name, `${name}--${theme}`]), [name, theme]); + const [isShow] = useDefault(visible, defaultVisible, noop); + const rootClassName = usePrefixClass('notice-bar'); + const containerClassName = classNames(rootClassName, `${rootClassName}--${theme}`); + const { animationSettingValue, updateScroll, updateAnimationFrame } = useAnimationSettingValue(); - const computedPrefixIcon: TdNoticeBarProps['prefixIcon'] | IconType | null = useMemo(() => { - let temp = null; - if (prefixIcon !== '') { - if (Object.keys(defaultIcons).includes(theme)) { - temp = defaultIcons[theme]; + useEffect(() => { + if (!hasBeenExecute.current) { + if (isShow) { + hasBeenExecute.current = true; + handleScrolling(); } - - return prefixIcon || temp || null; + return; } - return null; - }, [prefixIcon, theme]); - - const handleClick = useCallback( - (trigger: NoticeBarTrigger) => { - onClick?.(trigger); - }, - [onClick], - ); + const timer = setTimeout(() => { + if (isShow) { + updateAnimationFrame({ + offset: animationSettingValue.current.listWidth, + duration: 0, + }); + handleScrolling(); + } + }, 0); - const animateStyle = useMemo( - () => ({ - transform: animationSettingValue.current.offset ? `translateX(${animationSettingValue.current.offset}px)` : '', - transitionDuration: `${animationSettingValue.current.duration}s`, - transitionTimingFunction: 'linear', - }), + return () => { + clearTimeout(timer); + }; // eslint-disable-next-line react-hooks/exhaustive-deps - [animationSettingValue.current.offset, animationSettingValue.current.duration], - ); - - const listDOM = useRef(null); - const itemDOM = useRef(null); - - const [isShow] = useDefault(visible, defaultVisible, onChange); + }, [isShow]); function handleScrolling() { // 过滤 marquee 为 false @@ -170,13 +159,16 @@ const NoticeBar: React.FC = (props) => { updateScrollState = { ...animationSettingValue.current.scroll, ...defaultReduceState().scroll, - marquee: true, + marquee, }; - } else { + } + if (isObject(marquee)) { + const curMarquee = marquee as NoticeBarMarquee; updateScrollState = { - ...animationSettingValue.current.scroll, - ...filterUndefinedValue(marquee), marquee: true, + loop: typeof curMarquee?.loop === 'undefined' ? updateScrollState.loop : curMarquee.loop, + speed: curMarquee.speed ?? updateScrollState.speed, + delay: curMarquee.delay ?? updateScrollState.delay, }; } @@ -225,90 +217,103 @@ const NoticeBar: React.FC = (props) => { }, 0); } - const listScrollDomCls = cls(`${name}__list`, { - [`${name}__list--scrolling`]: animationSettingValue.current.scroll.marquee, - }); - - const listItemScrollDomCls = cls(`${name}__item`, { - [`${name}__item-detail`]: showExtraText, - }); - - const renderPrefixIcon = useMemo( - () => - computedPrefixIcon ? ( -
handleClick('prefix-icon')}> - {computedPrefixIcon} -
- ) : null, - [handleClick, name, computedPrefixIcon], + const handleClick = (trigger: NoticeBarTrigger) => { + onClick?.(trigger); + }; + // 动画 + const animateStyle = useMemo( + () => ({ + transform: animationSettingValue.current.offset ? `translateX(${animationSettingValue.current.offset}px)` : '', + transitionDuration: `${animationSettingValue.current.duration}s`, + transitionTimingFunction: 'linear', + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [animationSettingValue.current.offset, animationSettingValue.current.duration], ); - function onClickExtra(e: React.MouseEvent) { - e.stopPropagation(); - handleClick('extra'); - } - - const itemDomStyle = animationSettingValue.current.scroll.marquee ? animateStyle : {}; - - const hasBeenExecute = useRef(false); - - useEffect(() => { - if (!hasBeenExecute.current) { - if (isShow) { - hasBeenExecute.current = true; - handleScrolling(); - } - return; + const renderPrefixIcon = () => { + const prefixIconContent = prefixIcon ? parseTNode(prefixIcon) : defaultIcons[theme]; + if (prefixIcon !== null && prefixIconContent) { + return ( +
handleClick('prefix-icon')}> + {prefixIconContent} +
+ ); } - onChange?.(isShow); - setTimeout(() => { - if (isShow) { - updateAnimationFrame({ - offset: animationSettingValue.current.listWidth, - duration: 0, - }); - handleScrolling(); - } - }, 0); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isShow]); - - if (!isShow) { return null; - } - - return ( -
-
- {renderPrefixIcon} -
-
-
- handleClick('content')}> - {content} - {showExtraText && ( - - {extra} - - )} - -
-
-
+ }; - {suffixIcon && ( -
handleClick('suffix-icon')}> - {suffixIcon} + const renderContent = () => { + const renderShowContent = () => parseTNode(content) || null; + const renderOperationContent = () => { + const operationContent = parseTNode(operation); + if (!operationContent) { + return null; + } + return ( + { + e.stopPropagation(); + handleClick('operation'); + }} + > + {operationContent} + + ); + }; + return ( +
handleClick('content')}> + {direction === 'vertical' && isArray(content) ? ( + + {content.map((item, index) => ( + +
{item}
+
+ ))} +
+ ) : ( +
+ {renderShowContent()} + {renderOperationContent()}
)}
+ ); + }; + + const renderSuffixIconContent = () => { + const suffixIconContent = parseTNode(suffixIcon); + if (!suffixIconContent) { + return null; + } + return ( +
handleClick('suffix-icon')}> + {suffixIconContent} +
+ ); + }; + return isShow ? ( +
+ {renderPrefixIcon()} + {renderContent()} + {renderSuffixIconContent()}
- ); + ) : null; }; NoticeBar.displayName = 'NoticeBar'; diff --git a/src/notice-bar/_example/base.tsx b/src/notice-bar/_example/base.tsx new file mode 100644 index 00000000..7631cf21 --- /dev/null +++ b/src/notice-bar/_example/base.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { NoticeBar } from 'tdesign-mobile-react'; + +export default function Base() { + return ; +} diff --git a/src/notice-bar/_example/controller.jsx b/src/notice-bar/_example/controller.jsx deleted file mode 100644 index 6e1d9b4f..00000000 --- a/src/notice-bar/_example/controller.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import { ArrowRightIcon, AppIcon } from 'tdesign-icons-react'; -import { NoticeBar, Toast } from 'tdesign-mobile-react'; -import TDemoBlock from '../../../site/mobile/components/DemoBlock'; - -export default function ControllerDemo() { - const handleClick = (context) => { - Toast({ message: `click: ${context}` }); - }; - - return ( -
- - } - onClick={handleClick} - /> - } - onClick={handleClick} - /> - -
- ); -} diff --git a/src/notice-bar/_example/custom.tsx b/src/notice-bar/_example/custom.tsx new file mode 100644 index 00000000..d716fa16 --- /dev/null +++ b/src/notice-bar/_example/custom.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { NoticeBar } from 'tdesign-mobile-react'; +import { ChevronRightIcon, SoundIcon } from 'tdesign-icons-react'; +import './style/index.less'; + +export default function Custom() { + return ( + } + suffixIcon={} + /> + ); +} diff --git a/src/notice-bar/_example/customization.tsx b/src/notice-bar/_example/customization.tsx new file mode 100644 index 00000000..4318b82b --- /dev/null +++ b/src/notice-bar/_example/customization.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Link, NoticeBar } from 'tdesign-mobile-react'; +import { CloseIcon } from 'tdesign-icons-react'; +import './style/index.less'; + +export default function Customization() { + return ( + + 详情 + + } + suffixIcon={} + /> + ); +} diff --git a/src/notice-bar/_example/event.jsx b/src/notice-bar/_example/event.jsx deleted file mode 100644 index 66ba54e9..00000000 --- a/src/notice-bar/_example/event.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useState } from 'react'; -import { CloseIcon } from 'tdesign-icons-react'; -import { NoticeBar, Toast, Button } from 'tdesign-mobile-react'; -import TDemoBlock from '../../../site/mobile/components/DemoBlock'; - -export default function EventDemo() { - const [visible, setVisible] = useState(false); - const handleClick = (context) => { - Toast({ message: `click: ${context}` }); - }; - - const handleChange = () => { - setVisible((prev) => !prev); - }; - - const handleConsole = (value) => { - console.log(value); - }; - - return ( -
- - - } - /> - -
- ); -} diff --git a/src/notice-bar/_example/event.tsx b/src/notice-bar/_example/event.tsx new file mode 100644 index 00000000..b9c5c1cc --- /dev/null +++ b/src/notice-bar/_example/event.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { NoticeBar, Toast } from 'tdesign-mobile-react'; +import { ChevronRightIcon } from 'tdesign-icons-react'; +import './style/index.less'; + +export default function Event() { + const handleClick = (context: string) => { + Toast({ message: `click:${context}` }); + }; + return ( + } + onClick={handleClick} + /> + ); +} diff --git a/src/notice-bar/_example/icon.jsx b/src/notice-bar/_example/icon.jsx deleted file mode 100644 index 7ab8a341..00000000 --- a/src/notice-bar/_example/icon.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { NoticeBar } from 'tdesign-mobile-react'; -import TDemoBlock from '../../../site/mobile/components/DemoBlock'; - -export default function IconDemo() { - return ( -
- - - -
- ); -} diff --git a/src/notice-bar/_example/icon.tsx b/src/notice-bar/_example/icon.tsx new file mode 100644 index 00000000..b378cea1 --- /dev/null +++ b/src/notice-bar/_example/icon.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { NoticeBar } from 'tdesign-mobile-react'; + +export default function IconDemo() { + return ; +} diff --git a/src/notice-bar/_example/mobile.jsx b/src/notice-bar/_example/mobile.jsx deleted file mode 100644 index 2761cfce..00000000 --- a/src/notice-bar/_example/mobile.jsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, { useState } from 'react'; -import { CloseIcon, ChevronRightIcon } from 'tdesign-icons-react'; -import { NoticeBar, Toast } from 'tdesign-mobile-react'; -import TDemoBlock from '../../../site/mobile/components/DemoBlock'; - -function MobileDemo() { - const [visible] = useState(true); - const iconFunc = ; - const arrowRight = ; - const handleClick = (context) => { - Toast({ message: `click:${context}` }); - }; - - return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - {' '} -
- ); -} - -export default MobileDemo; diff --git a/src/notice-bar/_example/mobile.tsx b/src/notice-bar/_example/mobile.tsx new file mode 100644 index 00000000..8f39a84d --- /dev/null +++ b/src/notice-bar/_example/mobile.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import BaseDemo from './base'; +import IconDemo from './icon'; +import SuffixIcon from './suffixIcon'; +import Event from './event'; +import CustomDemo from './custom'; +import Customization from './customization'; +import ThemeDemo from './theme'; +import ScrollDemo from './scrolling'; +import TDemoBlock from '../../../site/mobile/components/DemoBlock'; +import TDemoHeader from '../../../site/mobile/components/DemoHeader'; + +function MobileDemo() { + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +} + +export default MobileDemo; diff --git a/src/notice-bar/_example/multi-line.jsx b/src/notice-bar/_example/multi-line.jsx deleted file mode 100644 index aa0de456..00000000 --- a/src/notice-bar/_example/multi-line.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import { ArrowRightIcon, CloseIcon } from 'tdesign-icons-react'; -import { NoticeBar, Toast } from 'tdesign-mobile-react'; -import TDemoBlock from '../../../site/mobile/components/DemoBlock'; - -export default function MultiLineDemo() { - const closeIcon = ; - const arrowRightIcon = ; - - const handleClick = (context) => { - Toast({ message: `click:${context}` }); - }; - - return ( -
- - - - - - -
- ); -} diff --git a/src/notice-bar/_example/scrolling.jsx b/src/notice-bar/_example/scrolling.jsx deleted file mode 100644 index 74a976b1..00000000 --- a/src/notice-bar/_example/scrolling.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React, { useState } from 'react'; -import { NoticeBar } from 'tdesign-mobile-react'; -import TDemoBlock from '../../../site/mobile/components/DemoBlock'; - -export default function ScrollingDemo() { - const [visible] = useState(true); - - return ( -
- - - - -
- ); -} diff --git a/src/notice-bar/_example/scrolling.tsx b/src/notice-bar/_example/scrolling.tsx new file mode 100644 index 00000000..074c9ce5 --- /dev/null +++ b/src/notice-bar/_example/scrolling.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { NoticeBar } from 'tdesign-mobile-react'; +import './style/index.less'; +import { SoundIcon } from 'tdesign-icons-react'; + +export default function Scrolling() { + const content = '提示文字描述提示文字描述提示文字描述提示文字描述文'; + const content1 = ['君不见', '高堂明镜悲白发', '朝如青丝暮成雪', '人生得意须尽欢', '莫使金樽空对月']; + + return ( + <> + + + } /> + + ); +} diff --git a/src/notice-bar/_example/static.jsx b/src/notice-bar/_example/static.jsx deleted file mode 100644 index 96bf575a..00000000 --- a/src/notice-bar/_example/static.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import React, { useState } from 'react'; -import { NoticeBar } from 'tdesign-mobile-react'; -import TDemoBlock from '../../../site/mobile/components/DemoBlock'; - -export default function StaticDemo() { - const [visible] = useState(true); - - return ( -
- - - -
- ); -} diff --git a/src/notice-bar/_example/style/index.less b/src/notice-bar/_example/style/index.less new file mode 100644 index 00000000..254f28bb --- /dev/null +++ b/src/notice-bar/_example/style/index.less @@ -0,0 +1,23 @@ +.cover-class { + background-color: var(--bg-color-demo, #fff); + + .t-icon { + color: var(--td-text-color-primary, rgba(0, 0, 0, 0.9)) !important; + } + + .t-notice-bar__text { + color: var(--bg-color-demo, #fff) !important; + } +} + +.custom-link { + margin-left: 2px; +} + +.notice-bar-demo-block { + margin-bottom: 16px; +} + +.tdesign-mobile-demo { + background-color: var(--bg-color-demo, #fff); +} diff --git a/src/notice-bar/_example/suffixIcon.tsx b/src/notice-bar/_example/suffixIcon.tsx new file mode 100644 index 00000000..c91af8c0 --- /dev/null +++ b/src/notice-bar/_example/suffixIcon.tsx @@ -0,0 +1,7 @@ +import React from 'react'; +import { NoticeBar } from 'tdesign-mobile-react'; +import { CloseIcon } from 'tdesign-icons-react'; + +export default function SuffixIcon() { + return } />; +} diff --git a/src/notice-bar/_example/theme.jsx b/src/notice-bar/_example/theme.jsx deleted file mode 100644 index 39b89f53..00000000 --- a/src/notice-bar/_example/theme.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { NoticeBar } from 'tdesign-mobile-react'; -import TDemoBlock from '../../../site/mobile/components/DemoBlock'; - -export default function ThemeDemo() { - return ( -
- - - - - - -
- ); -} diff --git a/src/notice-bar/_example/theme.tsx b/src/notice-bar/_example/theme.tsx new file mode 100644 index 00000000..8129c096 --- /dev/null +++ b/src/notice-bar/_example/theme.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { NoticeBar } from 'tdesign-mobile-react'; +import './style/index.less'; + +export default function ThemeDemo() { + return ( + <> + + + + + + ); +} diff --git a/src/notice-bar/defaultProps.ts b/src/notice-bar/defaultProps.ts index a40c4a2f..7653f552 100644 --- a/src/notice-bar/defaultProps.ts +++ b/src/notice-bar/defaultProps.ts @@ -4,4 +4,9 @@ import { TdNoticeBarProps } from './type'; -export const noticeBarDefaultProps: TdNoticeBarProps = { marquee: false, theme: 'info', defaultVisible: false }; +export const noticeBarDefaultProps: TdNoticeBarProps = { + direction: 'horizontal', + marquee: false, + theme: 'info', + defaultVisible: false, +}; diff --git a/src/notice-bar/notice-bar.en-US.md b/src/notice-bar/notice-bar.en-US.md new file mode 100644 index 00000000..8ad8a151 --- /dev/null +++ b/src/notice-bar/notice-bar.en-US.md @@ -0,0 +1,22 @@ +:: BASE_DOC :: + +## API + + +### NoticeBar Props + +name | type | default | description | required +-- | -- | -- | -- | -- +className | String | - | className of component | N +style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N +content | TNode | - | Typescript:`string \| string[] \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +direction | String | horizontal | options: horizontal/vertical | N +marquee | Boolean / Object | false | Typescript:`boolean \| NoticeBarMarquee` `interface NoticeBarMarquee { speed?: number; loop?: number; delay?: number }`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/notice-bar/type.ts) | N +operation | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +prefixIcon | TElement | - | Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +suffixIcon | TElement | - | Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +theme | String | info | options: info/success/warning/error | N +visible | Boolean | false | \- | N +defaultVisible | Boolean | false | uncontrolled property | N +onChange | Function | | Typescript:`(value: boolean) => void`
`deprecated` | N +onClick | Function | | Typescript:`(trigger: NoticeBarTrigger) => void`
[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/notice-bar/type.ts)。
`type NoticeBarTrigger = 'prefix-icon' \| 'content' \| 'operation' \| 'suffix-icon';`
| N diff --git a/src/notice-bar/notice-bar.md b/src/notice-bar/notice-bar.md index 16c6039c..a41b6a35 100644 --- a/src/notice-bar/notice-bar.md +++ b/src/notice-bar/notice-bar.md @@ -2,19 +2,21 @@ ## API + ### NoticeBar Props -名称 | 类型 | 默认值 | 说明 | 必传 +名称 | 类型 | 默认值 | 描述 | 必传 -- | -- | -- | -- | -- className | String | - | 类名 | N style | Object | - | 样式,TS 类型:`React.CSSProperties` | N -content | TNode | - | 文本内容。TS 类型:`string | TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N -extra | TNode | - | 右侧额外信息。TS 类型:`string | TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N -marquee | Boolean / Object | false | 跑马灯效果。speed 指速度控制;loop 指循环播放次数,值为 -1 表示循环播放,值为 0 表示不循环播放;delay 表示延迟多久开始播放。TS 类型:`boolean | DrawMarquee` `interface DrawMarquee { speed?: number; loop?: number; delay?: number }`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/notice-bar/type.ts) | N -prefixIcon | TNode | - | 前缀图标。TS 类型:`string | TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +content | TNode | - | 文本内容。TS 类型:`string \| string[] \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +direction | String | horizontal | 滚动方向。可选项:horizontal/vertical | N +marquee | Boolean / Object | false | 跑马灯效果。speed 指速度控制;loop 指循环播放次数,值为 -1 表示循环播放,值为 0 表示不循环播放;delay 表示延迟多久开始播放。TS 类型:`boolean \| NoticeBarMarquee` `interface NoticeBarMarquee { speed?: number; loop?: number; delay?: number }`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/notice-bar/type.ts) | N +operation | TNode | - | 右侧额外信息。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +prefixIcon | TElement | - | 前缀图标。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N suffixIcon | TElement | - | 后缀图标。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N theme | String | info | 内置主题。可选项:info/success/warning/error | N visible | Boolean | false | 显示/隐藏 | N defaultVisible | Boolean | false | 显示/隐藏。非受控属性 | N -onChange | Function | | TS 类型:`(value: boolean) => void`
展示或关闭公告栏时触发。参数为true时,代表展示公告栏。参数为false时,代表关闭公告栏 | N -onClick | Function | | TS 类型:`(trigger: NoticeBarTrigger) => void`
点击事件。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/notice-bar/type.ts)。
`type NoticeBarTrigger = 'prefix-icon' | 'content' | 'extra' | 'suffix-icon';`
| N +onChange | Function | | TS 类型:`(value: boolean) => void`
已废弃。展示或关闭公告栏时触发。参数为true时,代表展示公告栏。参数为false时,代表关闭公告栏 | N +onClick | Function | | TS 类型:`(trigger: NoticeBarTrigger) => void`
点击事件。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/notice-bar/type.ts)。
`type NoticeBarTrigger = 'prefix-icon' \| 'content' \| 'operation' \| 'suffix-icon';`
| N diff --git a/src/notice-bar/style/index.js b/src/notice-bar/style/index.js index 9ce2be11..fc4c44cc 100644 --- a/src/notice-bar/style/index.js +++ b/src/notice-bar/style/index.js @@ -1 +1 @@ -import '../../_common/style/mobile/components/notice-bar/_index.less'; +import '../../_common/style/mobile/components/notice-bar/v2/_index.less'; diff --git a/src/notice-bar/type.ts b/src/notice-bar/type.ts index 3e347002..f073008f 100644 --- a/src/notice-bar/type.ts +++ b/src/notice-bar/type.ts @@ -10,20 +10,25 @@ export interface TdNoticeBarProps { /** * 文本内容 */ - content?: TNode; + content?: [] | TNode; /** - * 右侧额外信息 + * 滚动方向 + * @default horizontal */ - extra?: TNode; + direction?: 'horizontal' | 'vertical'; /** * 跑马灯效果。speed 指速度控制;loop 指循环播放次数,值为 -1 表示循环播放,值为 0 表示不循环播放;delay 表示延迟多久开始播放 * @default false */ - marquee?: boolean | DrawMarquee; + marquee?: boolean | NoticeBarMarquee; + /** + * 右侧额外信息 + */ + operation?: TNode; /** * 前缀图标 */ - prefixIcon?: TNode; + prefixIcon?: TElement; /** * 后缀图标 */ @@ -43,20 +48,16 @@ export interface TdNoticeBarProps { * @default false */ defaultVisible?: boolean; - /** - * 展示或关闭公告栏时触发。参数为true时,代表展示公告栏。参数为false时,代表关闭公告栏 - */ - onChange?: (value: boolean) => void; /** * 点击事件 */ onClick?: (trigger: NoticeBarTrigger) => void; } -export interface DrawMarquee { +export interface NoticeBarMarquee { speed?: number; loop?: number; delay?: number; } -export type NoticeBarTrigger = 'prefix-icon' | 'content' | 'extra' | 'suffix-icon'; +export type NoticeBarTrigger = 'prefix-icon' | 'content' | 'operation' | 'suffix-icon'; diff --git a/test/snap/__snapshots__/csr.test.jsx.snap b/test/snap/__snapshots__/csr.test.jsx.snap index d4c5db5b..73822122 100644 --- a/test/snap/__snapshots__/csr.test.jsx.snap +++ b/test/snap/__snapshots__/csr.test.jsx.snap @@ -20121,6 +20121,1218 @@ exports[`csr snapshot test > csr test src/navbar/_example/size.tsx 1`] = `
`; +exports[`csr snapshot test > csr test src/notice-bar/_example/base.tsx 1`] = ` +
+
+
+
+ 这是一条普通的通知消息 +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/notice-bar/_example/custom.tsx 1`] = ` +
+
+
+ + + +
+
+
+ 提示文字描述提示文字描述提示文字描述 +
+
+
+ + + +
+
+
+`; + +exports[`csr snapshot test > csr test src/notice-bar/_example/customization.tsx 1`] = ` +
+
+
+ + + +
+
+
+ 提示文字描述提示文字描述提示文字描述提示文字描述提示文字描述提示文字描述 + + + + 详情 + + + +
+
+
+ + + +
+
+
+`; + +exports[`csr snapshot test > csr test src/notice-bar/_example/event.tsx 1`] = ` +
+
+
+ + + +
+
+
+ 这是一条普通的消息通知 +
+
+
+ + + +
+
+
+`; + +exports[`csr snapshot test > csr test src/notice-bar/_example/icon.tsx 1`] = ` +
+
+
+ + + +
+
+
+ 提示文字描述提示文字描述提示文字描述 +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/notice-bar/_example/mobile.tsx 1`] = ` +
+
+
+

+ NoticeBar 公告栏 +

+

+ 在导航栏下方,用于给用户显示提示消息。 +

+
+
+
+

+ 01 组件类型 +

+

+ 纯文字的公告栏 +

+
+
+
+
+
+ 这是一条普通的通知消息 +
+
+
+
+
+
+
+

+ 带图标静态公告栏 +

+
+
+
+
+ + + +
+
+
+ 提示文字描述提示文字描述提示文字描述 +
+
+
+
+
+
+
+

+ 带关闭的公告栏 +

+
+
+
+
+ + + +
+
+
+ 这是一条普通的通知信息 +
+
+
+ + + +
+
+
+
+
+
+

+ 带入口的公告栏 +

+
+
+
+
+ + + +
+
+
+ 这是一条普通的消息通知 +
+
+
+ + + +
+
+
+
+
+
+

+ 自定样式的公告栏 +

+
+
+
+
+ + + +
+
+
+ 提示文字描述提示文字描述提示文字描述 +
+
+
+ + + +
+
+
+
+
+
+

+ 自定义内容的公告栏 +

+
+
+
+
+ + + +
+
+
+ 提示文字描述提示文字描述提示文字描述提示文字描述提示文字描述提示文字描述 + + + + 详情 + + + +
+
+
+ + + +
+
+
+
+
+
+

+ 02 组件状态 +

+

+ 公告栏类型有普通(info)、警示(warning)、成功(success)、错误(error) +

+
+
+
+
+ + + +
+
+
+ 默认状态公告栏默认状态公告栏 +
+
+
+
+
+ + + +
+
+
+ 成功状态公告栏成功状态公告栏 +
+
+
+
+
+ + + +
+
+
+ 警示状态公告栏警示状态公告栏 +
+
+
+
+
+ + + +
+
+
+ 错误状态公告栏错误状态公告栏 +
+
+
+
+
+
+
+

+ 03 可滚动的公告栏 +

+

+ 可滚动公告栏有水平 (horizontal) 和垂直 (vertical) +

+
+
+
+
+
+ 提示文字描述提示文字描述提示文字描述提示文字描述文 +
+
+
+
+
+ + + +
+
+
+ 提示文字描述提示文字描述提示文字描述提示文字描述文 +
+
+
+
+
+ + + +
+
+
+
+
+
+ 莫使金樽空对月 +
+
+
+
+ 君不见 +
+
+
+
+ 高堂明镜悲白发 +
+
+
+
+ 朝如青丝暮成雪 +
+
+
+
+ 人生得意须尽欢 +
+
+
+
+ 莫使金樽空对月 +
+
+
+
+ 君不见 +
+
+
+
+
+
+
+
+
+
+`; + +exports[`csr snapshot test > csr test src/notice-bar/_example/scrolling.tsx 1`] = ` +
+
+
+
+ 提示文字描述提示文字描述提示文字描述提示文字描述文 +
+
+
+
+
+ + + +
+
+
+ 提示文字描述提示文字描述提示文字描述提示文字描述文 +
+
+
+
+
+ + + +
+
+
+
+
+
+ 莫使金樽空对月 +
+
+
+
+ 君不见 +
+
+
+
+ 高堂明镜悲白发 +
+
+
+
+ 朝如青丝暮成雪 +
+
+
+
+ 人生得意须尽欢 +
+
+
+
+ 莫使金樽空对月 +
+
+
+
+ 君不见 +
+
+
+
+
+
+
+`; + +exports[`csr snapshot test > csr test src/notice-bar/_example/suffixIcon.tsx 1`] = ` +
+
+
+ + + +
+
+
+ 这是一条普通的通知信息 +
+
+
+ + + +
+
+
+`; + +exports[`csr snapshot test > csr test src/notice-bar/_example/theme.tsx 1`] = ` +
+
+
+ + + +
+
+
+ 默认状态公告栏默认状态公告栏 +
+
+
+
+
+ + + +
+
+
+ 成功状态公告栏成功状态公告栏 +
+
+
+
+
+ + + +
+
+
+ 警示状态公告栏警示状态公告栏 +
+
+
+
+
+ + + +
+
+
+ 错误状态公告栏错误状态公告栏 +
+
+
+
+`; + exports[`csr snapshot test > csr test src/overlay/_example/base.tsx 1`] = `
@@ -25194,6 +26406,24 @@ exports[`ssr snapshot test > ssr test src/navbar/_example/search.tsx 1`] = `" ssr test src/navbar/_example/size.tsx 1`] = `"
标题文字
标题左对齐
大尺寸标题
"`; +exports[`ssr snapshot test > ssr test src/notice-bar/_example/base.tsx 1`] = `"
这是一条普通的通知消息
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/custom.tsx 1`] = `"
提示文字描述提示文字描述提示文字描述
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/customization.tsx 1`] = `"
提示文字描述提示文字描述提示文字描述提示文字描述提示文字描述提示文字描述详情
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/event.tsx 1`] = `"
这是一条普通的消息通知
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/icon.tsx 1`] = `"
提示文字描述提示文字描述提示文字描述
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/mobile.tsx 1`] = `"

NoticeBar 公告栏

在导航栏下方,用于给用户显示提示消息。

01 组件类型

纯文字的公告栏

这是一条普通的通知消息

带图标静态公告栏

提示文字描述提示文字描述提示文字描述

带关闭的公告栏

这是一条普通的通知信息

带入口的公告栏

这是一条普通的消息通知

自定样式的公告栏

提示文字描述提示文字描述提示文字描述

自定义内容的公告栏

提示文字描述提示文字描述提示文字描述提示文字描述提示文字描述提示文字描述详情

02 组件状态

公告栏类型有普通(info)、警示(warning)、成功(success)、错误(error)

默认状态公告栏默认状态公告栏
成功状态公告栏成功状态公告栏
警示状态公告栏警示状态公告栏
错误状态公告栏错误状态公告栏

03 可滚动的公告栏

可滚动公告栏有水平 (horizontal) 和垂直 (vertical)

提示文字描述提示文字描述提示文字描述提示文字描述文
提示文字描述提示文字描述提示文字描述提示文字描述文
莫使金樽空对月
君不见
高堂明镜悲白发
朝如青丝暮成雪
人生得意须尽欢
莫使金樽空对月
君不见
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/scrolling.tsx 1`] = `"
提示文字描述提示文字描述提示文字描述提示文字描述文
提示文字描述提示文字描述提示文字描述提示文字描述文
莫使金樽空对月
君不见
高堂明镜悲白发
朝如青丝暮成雪
人生得意须尽欢
莫使金樽空对月
君不见
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/suffixIcon.tsx 1`] = `"
这是一条普通的通知信息
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/theme.tsx 1`] = `"
默认状态公告栏默认状态公告栏
成功状态公告栏成功状态公告栏
警示状态公告栏警示状态公告栏
错误状态公告栏错误状态公告栏
"`; + exports[`ssr snapshot test > ssr test src/overlay/_example/base.tsx 1`] = `"
"`; exports[`ssr snapshot test > ssr test src/overlay/_example/index.tsx 1`] = `"

Overlay 遮罩层

通过遮罩层,可以强调部分内容

01 组件

基础遮罩层

"`; diff --git a/test/snap/__snapshots__/ssr.test.jsx.snap b/test/snap/__snapshots__/ssr.test.jsx.snap index a9fa0e2d..c6605a6f 100644 --- a/test/snap/__snapshots__/ssr.test.jsx.snap +++ b/test/snap/__snapshots__/ssr.test.jsx.snap @@ -174,6 +174,24 @@ exports[`ssr snapshot test > ssr test src/navbar/_example/search.tsx 1`] = `" ssr test src/navbar/_example/size.tsx 1`] = `"
标题文字
标题左对齐
大尺寸标题
"`; +exports[`ssr snapshot test > ssr test src/notice-bar/_example/base.tsx 1`] = `"
这是一条普通的通知消息
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/custom.tsx 1`] = `"
提示文字描述提示文字描述提示文字描述
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/customization.tsx 1`] = `"
提示文字描述提示文字描述提示文字描述提示文字描述提示文字描述提示文字描述详情
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/event.tsx 1`] = `"
这是一条普通的消息通知
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/icon.tsx 1`] = `"
提示文字描述提示文字描述提示文字描述
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/mobile.tsx 1`] = `"

NoticeBar 公告栏

在导航栏下方,用于给用户显示提示消息。

01 组件类型

纯文字的公告栏

这是一条普通的通知消息

带图标静态公告栏

提示文字描述提示文字描述提示文字描述

带关闭的公告栏

这是一条普通的通知信息

带入口的公告栏

这是一条普通的消息通知

自定样式的公告栏

提示文字描述提示文字描述提示文字描述

自定义内容的公告栏

提示文字描述提示文字描述提示文字描述提示文字描述提示文字描述提示文字描述详情

02 组件状态

公告栏类型有普通(info)、警示(warning)、成功(success)、错误(error)

默认状态公告栏默认状态公告栏
成功状态公告栏成功状态公告栏
警示状态公告栏警示状态公告栏
错误状态公告栏错误状态公告栏

03 可滚动的公告栏

可滚动公告栏有水平 (horizontal) 和垂直 (vertical)

提示文字描述提示文字描述提示文字描述提示文字描述文
提示文字描述提示文字描述提示文字描述提示文字描述文
莫使金樽空对月
君不见
高堂明镜悲白发
朝如青丝暮成雪
人生得意须尽欢
莫使金樽空对月
君不见
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/scrolling.tsx 1`] = `"
提示文字描述提示文字描述提示文字描述提示文字描述文
提示文字描述提示文字描述提示文字描述提示文字描述文
莫使金樽空对月
君不见
高堂明镜悲白发
朝如青丝暮成雪
人生得意须尽欢
莫使金樽空对月
君不见
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/suffixIcon.tsx 1`] = `"
这是一条普通的通知信息
"`; + +exports[`ssr snapshot test > ssr test src/notice-bar/_example/theme.tsx 1`] = `"
默认状态公告栏默认状态公告栏
成功状态公告栏成功状态公告栏
警示状态公告栏警示状态公告栏
错误状态公告栏错误状态公告栏
"`; + exports[`ssr snapshot test > ssr test src/overlay/_example/base.tsx 1`] = `"
"`; exports[`ssr snapshot test > ssr test src/overlay/_example/index.tsx 1`] = `"

Overlay 遮罩层

通过遮罩层,可以强调部分内容

01 组件

基础遮罩层

"`; From 8a52e8859e7c3481ddd5eb1202014628ad2d0246 Mon Sep 17 00:00:00 2001 From: Toby Date: Wed, 28 Aug 2024 10:54:41 +0800 Subject: [PATCH 07/28] =?UTF-8?q?feat(TabBar):=20TabBar=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=20mobile-vue=20(#482)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 更新样式文件 * feat(Demo): 升级TabBar示例 * fix(TabBar-type): 修复TabBar类型错误 * fix(TabBar-type): 同步新类型 * feat(TabBarItem): 增加tab-bar-item/样式 * feat(Demo): 升级TabBar示例 * feat(Demo): 组件TabBar自定义示例升级 * feat(TabBar-type): 增加TabBarContext类型 * feat(TabBar): 完成TabBar逻辑迁移 * feat(TabBar): 加上parseTNode * chore: 更新snap * feat(TabBar-API): 同步TabBar的API文档 * chore: update common * chore(TabBar): update snap * chore(TabBar): sync common file * chore: 变更common分支 * chore(TabBar): update tdesign-api * fix: 去掉旧的默认值定义 * refactor(TabBar): 改用usePrefixClass、useDefaultProps * chore(TabBar): update api * chore: 更改common到develop * refactor(TabBar): 简化逻辑 --------- Co-authored-by: github-actions[bot] --- site/mobile/mobile.config.js | 2 +- src/_common | 2 +- src/tab-bar/TabBar.tsx | 46 +- src/tab-bar/TabBarContext.tsx | 14 +- src/tab-bar/TabBarItem.tsx | 145 +- src/tab-bar/_example/badge-props.tsx | 41 + .../_example/{badge-props.jsx => base.tsx} | 16 +- src/tab-bar/_example/{base.jsx => custom.tsx} | 16 +- .../_example/{mobile.jsx => mobile.tsx} | 24 +- .../_example/{pure-icon.jsx => pure-icon.tsx} | 13 +- src/tab-bar/_example/round.tsx | 34 + src/tab-bar/_example/style/index.less | 20 + src/tab-bar/_example/text-spread.jsx | 47 - src/tab-bar/_example/text-spread.tsx | 49 + src/tab-bar/_example/{text.jsx => text.tsx} | 16 +- src/tab-bar/defaultProps.ts | 15 + src/tab-bar/style/index.js | 3 +- src/tab-bar/tab-bar.en-US.md | 50 + src/tab-bar/tab-bar.md | 38 +- src/tab-bar/type.ts | 45 +- test/snap/__snapshots__/csr.test.jsx.snap | 1760 +++++++++++++++++ test/snap/__snapshots__/ssr.test.jsx.snap | 16 + 22 files changed, 2235 insertions(+), 177 deletions(-) create mode 100644 src/tab-bar/_example/badge-props.tsx rename src/tab-bar/_example/{badge-props.jsx => base.tsx} (50%) rename src/tab-bar/_example/{base.jsx => custom.tsx} (56%) rename src/tab-bar/_example/{mobile.jsx => mobile.tsx} (58%) rename src/tab-bar/_example/{pure-icon.jsx => pure-icon.tsx} (53%) create mode 100644 src/tab-bar/_example/round.tsx delete mode 100644 src/tab-bar/_example/text-spread.jsx create mode 100644 src/tab-bar/_example/text-spread.tsx rename src/tab-bar/_example/{text.jsx => text.tsx} (59%) create mode 100644 src/tab-bar/defaultProps.ts create mode 100644 src/tab-bar/tab-bar.en-US.md diff --git a/site/mobile/mobile.config.js b/site/mobile/mobile.config.js index 5268ecd2..6d827833 100644 --- a/site/mobile/mobile.config.js +++ b/site/mobile/mobile.config.js @@ -210,7 +210,7 @@ export default { { title: 'TabBar 标签栏', name: 'tab-bar', - component: () => import('tdesign-mobile-react/tab-bar/_example/mobile.jsx'), + component: () => import('tdesign-mobile-react/tab-bar/_example/mobile.tsx'), }, { title: 'Fab 悬浮按钮', diff --git a/src/_common b/src/_common index 074d44f1..3e51c011 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit 074d44f1a1b06c4ee97759e3edf6cb3bbef2f61a +Subproject commit 3e51c01173a3a30cefe60dae7e802231c787d410 diff --git a/src/tab-bar/TabBar.tsx b/src/tab-bar/TabBar.tsx index 9cafeb0a..348c7883 100644 --- a/src/tab-bar/TabBar.tsx +++ b/src/tab-bar/TabBar.tsx @@ -1,47 +1,59 @@ import React, { forwardRef, memo, useMemo, useRef } from 'react'; import cls from 'classnames'; -import useConfig from '../_util/useConfig'; import useDefault from '../_util/useDefault'; import type { StyledProps } from '../common'; import type { TdTabBarProps } from './type'; import { TabBarProvider } from './TabBarContext'; +import parseTNode from '../_util/parseTNode'; +import useDefaultProps from '../hooks/useDefaultProps'; +import { usePrefixClass } from '../hooks/useClass'; +import { tabBarDefaultProps } from './defaultProps'; export interface TabBarProps extends TdTabBarProps, StyledProps {} -const TabBar = forwardRef((props, ref) => { - const { bordered, fixed, onChange, value, defaultValue } = props; - const { classPrefix } = useConfig(); - const name = `${classPrefix}-tab-bar`; +const TabBar = forwardRef((originProps, ref) => { + const props = useDefaultProps(originProps, tabBarDefaultProps); + const { bordered, fixed, onChange, value, defaultValue, safeAreaInsetBottom, shape, split, theme, children } = props; + + const tabBarClass = usePrefixClass('tab-bar'); const [activeValue, onToggleActiveValue] = useDefault(value, defaultValue, onChange); const defaultIndex = useRef(-1); const updateChild = onToggleActiveValue; - const tabBarClass = cls(name, { - [`${name}--bordered`]: bordered, - [`${name}--fixed`]: fixed, - }); + const itemCount = React.Children.count(parseTNode(children)); const memoProviderValues = useMemo( () => ({ defaultIndex, activeValue, updateChild, + shape, + split, + theme, + itemCount, }), - [defaultIndex, activeValue, updateChild], + [defaultIndex, activeValue, updateChild, shape, split, theme, itemCount], ); return ( -
- {props.children} +
+ {parseTNode(children)}
); }); -TabBar.defaultProps = { - bordered: true, - fixed: true, -}; - export default memo(TabBar); diff --git a/src/tab-bar/TabBarContext.tsx b/src/tab-bar/TabBarContext.tsx index 18a41018..36ab8646 100644 --- a/src/tab-bar/TabBarContext.tsx +++ b/src/tab-bar/TabBarContext.tsx @@ -1,11 +1,15 @@ import React, { createContext, MutableRefObject, useMemo } from 'react'; import { ChangeHandler } from '../_util/useDefault'; +import { TdTabBarProps } from './type'; -export const TabBarContext = createContext<{ - defaultIndex: MutableRefObject; - activeValue: number | string | (number | string)[]; - updateChild: ChangeHandler; -}>(null); +export const TabBarContext = createContext< + { + defaultIndex: MutableRefObject; + activeValue: number | string | (number | string)[]; + updateChild: ChangeHandler; + itemCount: number; + } & Pick +>(null); export function TabBarProvider({ children, value }) { const memoValue = useMemo(() => value, [value]); diff --git a/src/tab-bar/TabBarItem.tsx b/src/tab-bar/TabBarItem.tsx index 4d854e9c..a2a8dba1 100644 --- a/src/tab-bar/TabBarItem.tsx +++ b/src/tab-bar/TabBarItem.tsx @@ -1,24 +1,34 @@ import cls from 'classnames'; -import React, { forwardRef, memo, useContext, useEffect, useMemo, useState } from 'react'; - +import React, { forwardRef, memo, useContext, useEffect, useMemo, useRef, useState } from 'react'; import { CSSTransition } from 'react-transition-group'; +import { Icon } from 'tdesign-icons-react'; import type { StyledProps } from '../common'; import type { TdTabBarItemProps } from './type'; import { TabBarContext } from './TabBarContext'; import Badge from '../badge'; -import useConfig from '../_util/useConfig'; import useTabBarCssTransition from './useTabBarCssTransition'; +import parseTNode from '../_util/parseTNode'; +import useDefaultProps from '../hooks/useDefaultProps'; +import { usePrefixClass } from '../hooks/useClass'; export interface TabBarItemProps extends TdTabBarItemProps, StyledProps {} -const defaultBadgeOffset = [0, 5]; +const defaultBadgeOffset = [0, 0]; const defaultBadgeMaxCount = 99; -const TabBarItem = forwardRef((props, ref) => { +const TabBarItem = forwardRef((originProps, ref) => { + const props = useDefaultProps(originProps, {}); const { subTabBar, icon, badgeProps, value, children } = props; const hasSubTabBar = useMemo(() => !!subTabBar, [subTabBar]); - const { defaultIndex, activeValue, updateChild } = useContext(TabBarContext); + const { defaultIndex, activeValue, updateChild, shape, split, theme, itemCount } = useContext(TabBarContext); + + const tabBarItemClass = usePrefixClass('tab-bar-item'); + + const textNode = useRef(null); + + const [iconOnly, setIconOnly] = useState(false); + // 组件每次 render 生成一个临时的当前组件唯一值 const [currentName] = useState(() => { if (value) { @@ -27,9 +37,10 @@ const TabBarItem = forwardRef((props, ref) => { return (defaultIndex.current += 1); }); - const { classPrefix } = useConfig(); - - const componentName = `${classPrefix}-tab-bar-item`; + useEffect(() => { + const height = textNode?.current?.clientHeight; + setIconOnly(Number(height) === 0); + }, [textNode]); const [isSpread, setIsSpread] = useState(false); @@ -42,6 +53,8 @@ const TabBarItem = forwardRef((props, ref) => { const mergedBadgeProps = useMemo( () => ({ + count: 0, + dot: false, offset: defaultBadgeOffset, maxCount: defaultBadgeMaxCount, ...badgeProps, @@ -83,68 +96,92 @@ const TabBarItem = forwardRef((props, ref) => { setIsSpread(() => false); }; - const tabItemCls = cls(componentName, { - [`${classPrefix}-no-border`]: icon, - }); - const tabItemInnerCls = cls(`${componentName}__content`, { - [`${classPrefix}-is-checked`]: isChecked, - [`${componentName}--onlytext`]: !icon, + /** 拥挤否 */ + const crowded = itemCount > 3; + + const tabItemCls = cls( + tabBarItemClass, + { + [`${tabBarItemClass}--split`]: split, + [`${tabBarItemClass}--text-only`]: !icon, + [`${tabBarItemClass}--crowded`]: crowded, + }, + `${tabBarItemClass}--${shape}`, + ); + const tabItemInnerCls = cls( + `${tabBarItemClass}__content`, + { + [`${tabBarItemClass}__content--checked`]: isChecked, + }, + `${tabBarItemClass}__content--${theme}`, + ); + const tabItemTextCls = cls(`${tabBarItemClass}__text`, { + [`${tabBarItemClass}__text--small`]: icon, }); - const tabItemIconCls = cls(`${componentName}__icon`); - const tabItemSpreadCls = cls(`${componentName}__spread`); - const tabItemSpreadItemCls = cls(`${componentName}__spread-item`); - const tabItemTextCls = cls(`${componentName}__text`); - const tabItemIconMenuCls = cls(`${componentName}__icon-menu`); const transitionClsNames = useTabBarCssTransition({ name: 'spread', }); + const iconSize = `${iconOnly ? 24 : 20}px`; + + const iconContent = + icon && + React.cloneElement(icon, { + style: { fontSize: iconSize }, + }); + return ( -
-
+
+
{icon && ( -
+
{badgeProps && (badgeProps?.dot || badgeProps?.count) ? ( - + ) : ( - icon + iconContent )}
)} {children && ( -
- {shouldShowSubTabBar &&
} - {children} +
+ {shouldShowSubTabBar && ( + <> + +
+ + )} + {parseTNode(children)}
)} - - -
    - {subTabBar?.map((child, index) => ( -
  • { - e.stopPropagation(); - selectChild(child.value || index); - }} - > - {child.label} -
  • - ))} -
-
+ + +
    + {subTabBar?.map((child, index) => ( +
    { + e.stopPropagation(); + selectChild(child.value || index); + }} + > + {index !== 0 &&
    } +
    {child.label}
    +
    + ))} +
+
); }); diff --git a/src/tab-bar/_example/badge-props.tsx b/src/tab-bar/_example/badge-props.tsx new file mode 100644 index 00000000..b844fda6 --- /dev/null +++ b/src/tab-bar/_example/badge-props.tsx @@ -0,0 +1,41 @@ +import React, { useState, useEffect } from 'react'; +import { TabBar, TabBarItem } from 'tdesign-mobile-react'; +import { Icon } from 'tdesign-icons-react'; + +function TabBarBaseDemo() { + const list = [ + { name: 'label_1', text: '首页', icon: 'home', badgeProps: { count: 16 }, ariaLabel: '首页,有16条消息' }, + { name: 'label_2', text: '软件', icon: 'app', badgeProps: { dot: true }, ariaLabel: '软件,有新的消息' }, + { name: 'label_3', text: '聊天', icon: 'chat', badgeProps: { count: 'New' }, ariaLabel: '聊天,New' }, + { name: 'label_4', text: '我的', icon: 'user', badgeProps: { count: '···' }, ariaLabel: '我的,有很多消息' }, + ]; + const [value, setValue] = useState('label_1'); + + const change = (changeValue) => { + setValue(changeValue); + console.log('TabBar 值改变为:', changeValue); + }; + + useEffect(() => { + console.log('当前值:', value); + }, [value]); + + return ( +
+ + {list.map((item, i) => ( + } + value={item.name} + badgeProps={item.badgeProps} + > + {item.text} + + ))} + +
+ ); +} + +export default TabBarBaseDemo; diff --git a/src/tab-bar/_example/badge-props.jsx b/src/tab-bar/_example/base.tsx similarity index 50% rename from src/tab-bar/_example/badge-props.jsx rename to src/tab-bar/_example/base.tsx index 8c6fab1b..2136f4ad 100644 --- a/src/tab-bar/_example/badge-props.jsx +++ b/src/tab-bar/_example/base.tsx @@ -1,13 +1,13 @@ import React, { useState, useEffect } from 'react'; import { TabBar, TabBarItem } from 'tdesign-mobile-react'; -import { AppIcon } from 'tdesign-icons-react'; +import { Icon } from 'tdesign-icons-react'; function TabBarBaseDemo() { const list = [ - { name: 'label_1', text: '文字', icon: , badgeProps: { count: 16 } }, - { name: 'label_2', text: '文字', icon: , badgeProps: { dot: true } }, - { name: 'label_3', text: '文字', icon: , badgeProps: { count: 'New' } }, - { name: 'label_4', text: '文字', icon: , badgeProps: { count: '···' } }, + { value: 'label_1', label: '首页', icon: 'home' }, + { value: 'label_2', label: '应用', icon: 'app' }, + { value: 'label_3', label: '聊天', icon: 'chat' }, + { value: 'label_4', label: '我的', icon: 'user' }, ]; const [value, setValue] = useState('label_1'); @@ -22,10 +22,10 @@ function TabBarBaseDemo() { return (
- + {list.map((item, i) => ( - - {item.text} + } value={item.value}> + {item.label} ))} diff --git a/src/tab-bar/_example/base.jsx b/src/tab-bar/_example/custom.tsx similarity index 56% rename from src/tab-bar/_example/base.jsx rename to src/tab-bar/_example/custom.tsx index a3a46da6..a8fb00eb 100644 --- a/src/tab-bar/_example/base.jsx +++ b/src/tab-bar/_example/custom.tsx @@ -1,13 +1,13 @@ import React, { useState, useEffect } from 'react'; import { TabBar, TabBarItem } from 'tdesign-mobile-react'; -import { AppIcon } from 'tdesign-icons-react'; +import { Icon } from 'tdesign-icons-react'; function TabBarBaseDemo() { const list = [ - { name: 'label_1', text: '文字', icon: }, - { name: 'label_2', text: '文字', icon: }, - { name: 'label_3', text: '文字', icon: }, - { name: 'label_4', text: '文字', icon: }, + { value: 'label_1', icon: 'home', ariaLabel: '首页' }, + { value: 'label_2', icon: 'app', ariaLabel: '软件' }, + { value: 'label_3', icon: 'chat', ariaLabel: '聊天' }, + { value: 'label_4', icon: 'user', ariaLabel: '我的' }, ]; const [value, setValue] = useState('label_1'); @@ -21,12 +21,10 @@ function TabBarBaseDemo() { }, [value]); return ( -
+
{list.map((item, i) => ( - - {item.text} - + } value={item.value} /> ))}
diff --git a/src/tab-bar/_example/mobile.jsx b/src/tab-bar/_example/mobile.tsx similarity index 58% rename from src/tab-bar/_example/mobile.jsx rename to src/tab-bar/_example/mobile.tsx index d7e63e98..46108059 100644 --- a/src/tab-bar/_example/mobile.jsx +++ b/src/tab-bar/_example/mobile.tsx @@ -7,28 +7,36 @@ import BadgePropsDemo from './badge-props'; import TextDemo from './text'; import PureIconDemo from './pure-icon'; import TextSpreadDemo from './text-spread'; +import RoundDemo from './round'; +import CustomDemo from './custom'; import './style/index.less'; function TabBarMobileDemo() { return (
- - - - - - - - + + + + + + + + + + + + + +
); } diff --git a/src/tab-bar/_example/pure-icon.jsx b/src/tab-bar/_example/pure-icon.tsx similarity index 53% rename from src/tab-bar/_example/pure-icon.jsx rename to src/tab-bar/_example/pure-icon.tsx index 813d0479..06a2f07a 100644 --- a/src/tab-bar/_example/pure-icon.jsx +++ b/src/tab-bar/_example/pure-icon.tsx @@ -1,9 +1,14 @@ import React, { useState, useEffect } from 'react'; import { TabBar, TabBarItem } from 'tdesign-mobile-react'; -import { AppIcon } from 'tdesign-icons-react'; +import { Icon } from 'tdesign-icons-react'; function TabBarBaseDemo() { - const list = [{ name: 'label_1' }, { name: 'label_2' }, { name: 'label_3' }, { name: 'label_4' }]; + const list = [ + { value: 'label_1', icon: 'home', ariaLabel: '首页' }, + { value: 'label_2', icon: 'app', ariaLabel: '软件' }, + { value: 'label_3', icon: 'chat', ariaLabel: '聊天' }, + { value: 'label_4', icon: 'user', ariaLabel: '我的' }, + ]; const [value, setValue] = useState('label_1'); const change = (changeValue) => { @@ -17,9 +22,9 @@ function TabBarBaseDemo() { return (
- + {list.map((item, i) => ( - } value={item.name} /> + } value={item.value} /> ))}
diff --git a/src/tab-bar/_example/round.tsx b/src/tab-bar/_example/round.tsx new file mode 100644 index 00000000..201f1b23 --- /dev/null +++ b/src/tab-bar/_example/round.tsx @@ -0,0 +1,34 @@ +import React, { useState, useEffect } from 'react'; +import { TabBar, TabBarItem } from 'tdesign-mobile-react'; +import { Icon } from 'tdesign-icons-react'; + +function TabBarBaseDemo() { + const list = [ + { value: 'label_1', icon: 'home', ariaLabel: '首页' }, + { value: 'label_2', icon: 'app', ariaLabel: '软件' }, + { value: 'label_3', icon: 'chat', ariaLabel: '聊天' }, + { value: 'label_4', icon: 'user', ariaLabel: '我的' }, + ]; + const [value, setValue] = useState('label_1'); + + const change = (changeValue) => { + setValue(changeValue); + console.log('TabBar 值改变为:', changeValue); + }; + + useEffect(() => { + console.log('当前值:', value); + }, [value]); + + return ( +
+ + {list.map((item, i) => ( + } value={item.value} /> + ))} + +
+ ); +} + +export default TabBarBaseDemo; diff --git a/src/tab-bar/_example/style/index.less b/src/tab-bar/_example/style/index.less index 374185e0..21a757ef 100644 --- a/src/tab-bar/_example/style/index.less +++ b/src/tab-bar/_example/style/index.less @@ -5,4 +5,24 @@ .t-tab-bar + .t-tab-bar { margin-top: 16px; } + + .section { + &-custom { + --td-tab-bar-border-color: #e7e7e7; + --td-tab-bar-bg-color: #eee; + --td-tab-bar-hover-color: #ddd; + --td-tab-bar-item-color: #bbb; + --td-tab-bar-item-active-color: #333; + } + } +} + +:root[theme-mode='dark'] { + .tdesign-mobile-demo { + .section { + &-custom { + --td-tab-bar-color: rgba(0, 0, 0, 0.9); + } + } + } } diff --git a/src/tab-bar/_example/text-spread.jsx b/src/tab-bar/_example/text-spread.jsx deleted file mode 100644 index 0b4b2883..00000000 --- a/src/tab-bar/_example/text-spread.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; -import { TabBar, TabBarItem } from 'tdesign-mobile-react'; - -function TabBarBaseDemo() { - const list = [ - { - name: 'label_1', - text: '标签栏一', - }, - { - name: 'label_2', - text: '标签栏二', - }, - { - name: 'label_3', - text: '此处展开', - children: [ - { - value: 'spread_3', - label: '展开项三', - }, - { - value: 'spread_2', - label: '展开项二', - }, - { - value: 'spread_1', - label: '展开项一', - }, - ], - }, - ]; - - return ( -
- - {list.map((item, i) => ( - - {item.text} - - ))} - -
- ); -} - -export default TabBarBaseDemo; diff --git a/src/tab-bar/_example/text-spread.tsx b/src/tab-bar/_example/text-spread.tsx new file mode 100644 index 00000000..75698218 --- /dev/null +++ b/src/tab-bar/_example/text-spread.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { TabBar, TabBarItem } from 'tdesign-mobile-react'; + +function TabBarBaseDemo() { + const list = [ + { + value: 'home', + label: '首页', + icon: 'home', + }, + { + value: 'app', + label: '应用', + icon: 'app', + }, + { + value: 'user', + label: '我的', + children: [ + { + value: 'info', + label: '基本信息', + }, + { + value: 'home-page', + label: '个人主页', + }, + { + value: 'setting', + label: '设置', + }, + ], + }, + ]; + + return ( +
+ + {list.map((item, i) => ( + + {item.label} + + ))} + +
+ ); +} + +export default TabBarBaseDemo; diff --git a/src/tab-bar/_example/text.jsx b/src/tab-bar/_example/text.tsx similarity index 59% rename from src/tab-bar/_example/text.jsx rename to src/tab-bar/_example/text.tsx index 66c816bd..0cc38e73 100644 --- a/src/tab-bar/_example/text.jsx +++ b/src/tab-bar/_example/text.tsx @@ -3,12 +3,12 @@ import { TabBar, TabBarItem } from 'tdesign-mobile-react'; function TabBarBaseDemo() { const list = [ - { name: 'label_1', text: '标签栏一' }, - { name: 'label_2', text: '标签栏二' }, - { name: 'label_3', text: '标签栏三' }, - { name: 'label_4', text: '标签栏四' }, + { value: 'home', label: '首页' }, + { value: 'app', label: '应用' }, + { value: 'chat', label: '聊天' }, + { value: 'user', label: '我的' }, ]; - const [value, setValue] = useState('label_1'); + const [value, setValue] = useState('home'); const change = (changeValue) => { setValue(changeValue); @@ -21,10 +21,10 @@ function TabBarBaseDemo() { return (
- + {list.map((item, i) => ( - - {item.text} + + {item.label} ))} diff --git a/src/tab-bar/defaultProps.ts b/src/tab-bar/defaultProps.ts new file mode 100644 index 00000000..af85cd59 --- /dev/null +++ b/src/tab-bar/defaultProps.ts @@ -0,0 +1,15 @@ +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdTabBarProps } from './type'; + +export const tabBarDefaultProps: TdTabBarProps = { + bordered: true, + fixed: true, + safeAreaInsetBottom: true, + shape: 'normal', + split: true, + theme: 'normal', + defaultValue: undefined, +}; diff --git a/src/tab-bar/style/index.js b/src/tab-bar/style/index.js index 702d60a8..5d6c7d7d 100644 --- a/src/tab-bar/style/index.js +++ b/src/tab-bar/style/index.js @@ -1 +1,2 @@ -import '../../_common/style/mobile/components/tab-bar/_index.less'; +import '../../_common/style/mobile/components/tab-bar/v2/_index.less'; +import '../../_common/style/mobile/components/tab-bar-item/v2/_index.less'; diff --git a/src/tab-bar/tab-bar.en-US.md b/src/tab-bar/tab-bar.en-US.md new file mode 100644 index 00000000..087a8848 --- /dev/null +++ b/src/tab-bar/tab-bar.en-US.md @@ -0,0 +1,50 @@ +:: BASE_DOC :: + +## API + +### TabBar Props + +name | type | default | description | required +-- | -- | -- | -- | -- +className | String | - | className of component | N +style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N +bordered | Boolean | true | \- | N +children | TNode | - | Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +fixed | Boolean | true | \- | N +safeAreaInsetBottom | Boolean | true | \- | N +shape | String | 'normal' | options: normal/round | N +split | Boolean | true | \- | N +theme | String | 'normal' | options: normal/tag | N +value | String / Number / Array | undefined | Typescript:`string \| number \| Array` | N +defaultValue | String / Number / Array | undefined | uncontrolled property。Typescript:`string \| number \| Array` | N +onChange | Function | | Typescript:`(value: string \| number) => void`
| N + + +### TabBarItem Props + +name | type | default | description | required +-- | -- | -- | -- | -- +className | String | - | className of component | N +style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N +badgeProps | Object | - | Typescript:`BadgeProps`,[Badge API Documents](./badge?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/tab-bar/type.ts) | N +children | TNode | - | Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +icon | TElement | - | Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +subTabBar | Array | - | Typescript:`SubTabBarItem[] ` `interface SubTabBarItem { value: string; label: string }`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/tab-bar/type.ts) | N +value | String / Number | - | \- | N + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-tab-bar-active-bg | @brand-color-light | - +--td-tab-bar-active-color | @brand-color | - +--td-tab-bar-bg-color | @bg-color-container | - +--td-tab-bar-border-color | @border-color | - +--td-tab-bar-color | @font-gray-1 | - +--td-tab-bar-height | 40px | - +--td-tab-bar-hover-bg-color | rgba(0, 0, 0, .05) | - +--td-tab-bar-spread-border-color | @border-color | - +--td-tab-bar-spread-shadow | @shadow-3 | - +--td-tab-bar-border-color | @border-color | - +--td-tab-bar-round-shadow | @shadow-3 | - \ No newline at end of file diff --git a/src/tab-bar/tab-bar.md b/src/tab-bar/tab-bar.md index acb6981d..3b381250 100644 --- a/src/tab-bar/tab-bar.md +++ b/src/tab-bar/tab-bar.md @@ -4,23 +4,47 @@ ### TabBar Props -名称 | 类型 | 默认值 | 说明 | 必传 +名称 | 类型 | 默认值 | 描述 | 必传 -- | -- | -- | -- | -- className | String | - | 类名 | N style | Object | - | 样式,TS 类型:`React.CSSProperties` | N bordered | Boolean | true | 是否显示外边框 | N +children | TNode | - | 标签栏内容。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N fixed | Boolean | true | 是否固定在底部 | N -value | String / Number / Array | undefined | 当前选中标签的索引。TS 类型:`string | number | Array` | N -defaultValue | String / Number / Array | undefined | 当前选中标签的索引。非受控属性。TS 类型:`string | number | Array` | N -onChange | Function | | TS 类型:`() => void`
选中标签切换时触发 | N +safeAreaInsetBottom | Boolean | true | 是否为 iPhoneX 留出底部安全距离 | N +shape | String | 'normal' | 标签栏的形状。可选项:normal/round | N +split | Boolean | true | 是否需要分割线 | N +theme | String | 'normal' | 选项风格。可选项:normal/tag | N +value | String / Number / Array | undefined | 当前选中标签的索引。TS 类型:`string \| number \| Array` | N +defaultValue | String / Number / Array | undefined | 当前选中标签的索引。非受控属性。TS 类型:`string \| number \| Array` | N +onChange | Function | | TS 类型:`(value: string \| number) => void`
选中标签切换时触发 | N + ### TabBarItem Props -名称 | 类型 | 默认值 | 说明 | 必传 +名称 | 类型 | 默认值 | 描述 | 必传 -- | -- | -- | -- | -- className | String | - | 类名 | N style | Object | - | 样式,TS 类型:`React.CSSProperties` | N -badgeProps | Object | - | 图标右上角提示信息。TS 类型:`TdBadgeProps`,[Badge API Documents](./badge?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/tab-bar/type.ts) | N -icon | TNode | - | 图标名称。TS 类型:`string | TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +badgeProps | Object | - | 图标右上角提示信息。TS 类型:`BadgeProps`,[Badge API Documents](./badge?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/tab-bar/type.ts) | N +children | TNode | - | 标签内容。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +icon | TElement | - | 图标名称。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N subTabBar | Array | - | 二级菜单。TS 类型:`SubTabBarItem[] ` `interface SubTabBarItem { value: string; label: string }`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/tab-bar/type.ts) | N value | String / Number | - | 标识符 | N + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-tab-bar-active-bg | @brand-color-light | - +--td-tab-bar-active-color | @brand-color | - +--td-tab-bar-bg-color | @bg-color-container | - +--td-tab-bar-border-color | @border-color | - +--td-tab-bar-color | @font-gray-1 | - +--td-tab-bar-height | 40px | - +--td-tab-bar-hover-bg-color | rgba(0, 0, 0, .05) | - +--td-tab-bar-spread-border-color | @border-color | - +--td-tab-bar-spread-shadow | @shadow-3 | - +--td-tab-bar-border-color | @border-color | - +--td-tab-bar-round-shadow | @shadow-3 | - \ No newline at end of file diff --git a/src/tab-bar/type.ts b/src/tab-bar/type.ts index 004e9cf3..2dfbae2c 100644 --- a/src/tab-bar/type.ts +++ b/src/tab-bar/type.ts @@ -4,8 +4,8 @@ * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC * */ -import { TdBadgeProps } from '../badge'; -import { TNode } from '../common'; +import { BadgeProps } from '../badge'; +import { TNode, TElement } from '../common'; export interface TdTabBarProps { /** @@ -13,11 +13,35 @@ export interface TdTabBarProps { * @default true */ bordered?: boolean; + /** + * 标签栏内容 + */ + children?: TNode; /** * 是否固定在底部 * @default true */ fixed?: boolean; + /** + * 是否为 iPhoneX 留出底部安全距离 + * @default true + */ + safeAreaInsetBottom?: boolean; + /** + * 标签栏的形状 + * @default 'normal' + */ + shape?: 'normal' | 'round'; + /** + * 是否需要分割线 + * @default true + */ + split?: boolean; + /** + * 选项风格 + * @default 'normal' + */ + theme?: 'normal' | 'tag'; /** * 当前选中标签的索引 */ @@ -29,26 +53,33 @@ export interface TdTabBarProps { /** * 选中标签切换时触发 */ - onChange?: () => void; + onChange?: (value: string | number) => void; } export interface TdTabBarItemProps { /** * 图标右上角提示信息 */ - badgeProps?: TdBadgeProps; + badgeProps?: BadgeProps; + /** + * 标签内容 + */ + children?: TNode; /** * 图标名称 */ - icon?: TNode; + icon?: TElement; /** * 二级菜单 */ - subTabBar?: SubTabBarItem[] ; + subTabBar?: SubTabBarItem[]; /** * 标识符 */ value?: string | number; } -export interface SubTabBarItem { value: string; label: string }; +export interface SubTabBarItem { + value: string; + label: string; +} diff --git a/test/snap/__snapshots__/csr.test.jsx.snap b/test/snap/__snapshots__/csr.test.jsx.snap index 73822122..3da6f6d6 100644 --- a/test/snap/__snapshots__/csr.test.jsx.snap +++ b/test/snap/__snapshots__/csr.test.jsx.snap @@ -24677,6 +24677,1750 @@ exports[`csr snapshot test > csr test src/sticky/_example/offset.tsx 1`] = `
`; +exports[`csr snapshot test > csr test src/tab-bar/_example/badge-props.tsx 1`] = ` +
+
+
+
+ +
+
+ +
+ +
+
+ +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/tab-bar/_example/base.tsx 1`] = ` +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/tab-bar/_example/custom.tsx 1`] = ` +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/tab-bar/_example/mobile.tsx 1`] = ` +
+
+
+

+ TabBar 标签栏 +

+

+ 用于在不同功能模块之间进行快速切换,位于页面底部。 +

+
+
+
+

+ 01 组件类型 +

+

+ 纯文本标签栏 +

+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+

+ 图标加文字标签栏 +

+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+

+ 纯图标标签栏 +

+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+

+ 双层级纯文本标签栏 +

+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+

+ 01 组件类型 +

+

+ 弱选中标签栏 +

+
+
+
+
+
+ +
+
+ +
+ +
+
+ +
+
+
+
+
+
+
+

+ 悬浮胶囊标签栏 +

+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+

+ 03 自定义 +

+

+ 自定义样式 +

+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+`; + +exports[`csr snapshot test > csr test src/tab-bar/_example/pure-icon.tsx 1`] = ` +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/tab-bar/_example/round.tsx 1`] = ` +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/tab-bar/_example/text.tsx 1`] = ` +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/tab-bar/_example/text-spread.tsx 1`] = ` +
+
+
+
+ +
+
+ +
+
+ +
+
+
+`; + exports[`csr snapshot test > csr test src/tag/_example/checkable.tsx 1`] = `
ssr test src/sticky/_example/index.tsx 1`] = `"
ssr test src/sticky/_example/offset.tsx 1`] = `"
"`; +exports[`ssr snapshot test > ssr test src/tab-bar/_example/badge-props.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/base.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/custom.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/mobile.tsx 1`] = `"

TabBar 标签栏

用于在不同功能模块之间进行快速切换,位于页面底部。

01 组件类型

纯文本标签栏

图标加文字标签栏

纯图标标签栏

双层级纯文本标签栏

01 组件类型

弱选中标签栏

悬浮胶囊标签栏

03 自定义

自定义样式

"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/pure-icon.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/round.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/text.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/text-spread.tsx 1`] = `"
"`; + exports[`ssr snapshot test > ssr test src/tag/_example/checkable.tsx 1`] = `"
可选中的标签
light
未选中态已选中态
dark
未选中态已选中态
outline
未选中态已选中态
light-outline
未选中态已选中态
"`; exports[`ssr snapshot test > ssr test src/tag/_example/closable.tsx 1`] = `"
可关闭标签
文字标签文字标签
"`; diff --git a/test/snap/__snapshots__/ssr.test.jsx.snap b/test/snap/__snapshots__/ssr.test.jsx.snap index c6605a6f..b143a8d7 100644 --- a/test/snap/__snapshots__/ssr.test.jsx.snap +++ b/test/snap/__snapshots__/ssr.test.jsx.snap @@ -252,6 +252,22 @@ exports[`ssr snapshot test > ssr test src/sticky/_example/index.tsx 1`] = `"
ssr test src/sticky/_example/offset.tsx 1`] = `"
"`; +exports[`ssr snapshot test > ssr test src/tab-bar/_example/badge-props.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/base.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/custom.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/mobile.tsx 1`] = `"

TabBar 标签栏

用于在不同功能模块之间进行快速切换,位于页面底部。

01 组件类型

纯文本标签栏

图标加文字标签栏

纯图标标签栏

双层级纯文本标签栏

01 组件类型

弱选中标签栏

悬浮胶囊标签栏

03 自定义

自定义样式

"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/pure-icon.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/round.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/text.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/tab-bar/_example/text-spread.tsx 1`] = `"
"`; + exports[`ssr snapshot test > ssr test src/tag/_example/checkable.tsx 1`] = `"
可选中的标签
light
未选中态已选中态
dark
未选中态已选中态
outline
未选中态已选中态
light-outline
未选中态已选中态
"`; exports[`ssr snapshot test > ssr test src/tag/_example/closable.tsx 1`] = `"
可关闭标签
文字标签文字标签
"`; From 2cb121219a11ffa2c1cbc48327692abca4deaee7 Mon Sep 17 00:00:00 2001 From: Toby Date: Wed, 28 Aug 2024 11:17:47 +0800 Subject: [PATCH 08/28] =?UTF-8?q?feat(SideBar):=20=E5=88=9B=E5=BB=BASideBa?= =?UTF-8?q?r=E7=BB=84=E4=BB=B6=20(#491)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(SideBar): 升级SideBar样式 * feat(SideBar): 创建SideBar组件 * feat(SideBar): 创建SideBar文档 * fix(Grid): 补充Grid遗漏的type * chore: update snap * test(SideBar): 测试增加React路由 * test(SideBar): update snap * docs(SideBar): 解决演示用例样式冲突问题 * test(SideBar): update snap * docs(SideBar): 修复示例内容体高度样式 * refactor(SideBar): 按规范改用useDefaultProps、usePrefixClass * docs(SideBar): 移除多余API文档 * chore(SideBar): update tdesign-api * test: update snap * docs(SideBar): 删除多余示例参数 * chore(SideBar): update tdesign-api * chore: 变更common分支 * chore(SideBar): update tdesign-api * chore(SideBar): update tdesign-api * chore(SideBar): update api * perf(Demo): 示例头部的返回按钮优化为按需引入 * chore: update snapshot --------- Co-authored-by: github-actions[bot] --- site/mobile/components/Header.jsx | 11 +- site/mobile/mobile.config.js | 25 + site/web/site.config.js | 6 + src/grid/type.ts | 4 + src/side-bar/SideBar.tsx | 88 + src/side-bar/SideBarContext.tsx | 22 + src/side-bar/SideBarItem.tsx | 58 + .../__snapshots__/side-bar.test.tsx.snap | 3 + src/side-bar/__tests__/side-bar.test.tsx | 12 + src/side-bar/_example/base.tsx | 93 + src/side-bar/_example/custom.tsx | 93 + src/side-bar/_example/index.tsx | 36 + src/side-bar/_example/style/base.less | 31 + src/side-bar/_example/style/custom.less | 34 + src/side-bar/_example/style/index.less | 8 + src/side-bar/_example/style/switch.less | 35 + src/side-bar/_example/switch.tsx | 55 + src/side-bar/_example/with-icon.tsx | 95 + src/side-bar/defaultProps.ts | 9 + src/side-bar/index.ts | 16 + src/side-bar/side-bar.en-US.md | 46 + src/side-bar/side-bar.md | 46 + src/side-bar/style/css.js | 1 + src/side-bar/style/index.js | 1 + src/side-bar/type.ts | 56 + test/snap/__snapshots__/csr.test.jsx.snap | 5772 ++++++++++++++++- test/snap/__snapshots__/ssr.test.jsx.snap | 172 +- test/snap/csr.test.jsx | 3 +- test/snap/ssr.test.jsx | 3 +- 29 files changed, 6581 insertions(+), 253 deletions(-) create mode 100644 src/side-bar/SideBar.tsx create mode 100644 src/side-bar/SideBarContext.tsx create mode 100644 src/side-bar/SideBarItem.tsx create mode 100644 src/side-bar/__tests__/__snapshots__/side-bar.test.tsx.snap create mode 100644 src/side-bar/__tests__/side-bar.test.tsx create mode 100644 src/side-bar/_example/base.tsx create mode 100644 src/side-bar/_example/custom.tsx create mode 100644 src/side-bar/_example/index.tsx create mode 100644 src/side-bar/_example/style/base.less create mode 100644 src/side-bar/_example/style/custom.less create mode 100644 src/side-bar/_example/style/index.less create mode 100644 src/side-bar/_example/style/switch.less create mode 100644 src/side-bar/_example/switch.tsx create mode 100644 src/side-bar/_example/with-icon.tsx create mode 100644 src/side-bar/defaultProps.ts create mode 100644 src/side-bar/index.ts create mode 100644 src/side-bar/side-bar.en-US.md create mode 100644 src/side-bar/side-bar.md create mode 100644 src/side-bar/style/css.js create mode 100644 src/side-bar/style/index.js create mode 100644 src/side-bar/type.ts diff --git a/site/mobile/components/Header.jsx b/site/mobile/components/Header.jsx index 3aa8daa1..75785400 100644 --- a/site/mobile/components/Header.jsx +++ b/site/mobile/components/Header.jsx @@ -1,13 +1,22 @@ import React from 'react'; +import { ChevronLeftIcon } from 'tdesign-icons-react'; +import { useSearchParams, useNavigate } from 'react-router-dom'; const THeader = (prop) => { const { title } = prop; + const [searchParams] = useSearchParams(); + const navigate = useNavigate(); + + const showNavBack = !!searchParams.get('showNavBack'); + + const navBack = () => navigate(-1); + return ( <> {title ? (
{title}
- {/* */} + {showNavBack && }
) : null} diff --git a/site/mobile/mobile.config.js b/site/mobile/mobile.config.js index 6d827833..ed785fc0 100644 --- a/site/mobile/mobile.config.js +++ b/site/mobile/mobile.config.js @@ -147,6 +147,31 @@ export default { name: 'navbar', component: () => import('tdesign-mobile-react/navbar/_example/index.tsx'), }, + { + title: 'SideBar 侧边栏', + name: 'side-bar', + component: () => import('tdesign-mobile-react/side-bar/_example/index.tsx'), + }, + { + title: 'SideBar 侧边栏', + name: 'side-bar-base', + component: () => import('tdesign-mobile-react/side-bar/_example/base.tsx'), + }, + { + title: 'SideBar 侧边栏', + name: 'side-bar-switch', + component: () => import('tdesign-mobile-react/side-bar/_example/switch.tsx'), + }, + { + title: 'SideBar 侧边栏', + name: 'side-bar-with-icon', + component: () => import('tdesign-mobile-react/side-bar/_example/with-icon.tsx'), + }, + { + title: 'SideBar 侧边栏', + name: 'side-bar-custom', + component: () => import('tdesign-mobile-react/side-bar/_example/custom.tsx'), + }, { title: 'SwipeCell 滑动单元格', name: 'swipe-cell', diff --git a/site/web/site.config.js b/site/web/site.config.js index f709bd4a..008d134f 100644 --- a/site/web/site.config.js +++ b/site/web/site.config.js @@ -124,6 +124,12 @@ export default { path: '/mobile-react/components/navbar', component: () => import('tdesign-mobile-react/navbar/navbar.md'), }, + { + title: 'SideBar 侧边栏', + name: 'side-bar', + path: '/mobile-react/components/side-bar', + component: () => import('tdesign-mobile-react/side-bar/side-bar.md'), + }, { title: 'Tabs 选项卡', name: 'tabs', diff --git a/src/grid/type.ts b/src/grid/type.ts index 27523ce2..9e47fe29 100644 --- a/src/grid/type.ts +++ b/src/grid/type.ts @@ -32,6 +32,10 @@ export interface TdGridProps { * @default default */ theme?: 'default' | 'card'; + /** + * 标签栏内容 + */ + children?: TNode; } export interface TdGridItemProps { diff --git a/src/side-bar/SideBar.tsx b/src/side-bar/SideBar.tsx new file mode 100644 index 00000000..55e03f5d --- /dev/null +++ b/src/side-bar/SideBar.tsx @@ -0,0 +1,88 @@ +import React, { forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import type { StyledProps } from '../common'; +import type { TdSideBarProps } from './type'; +import { sideBarDefaultProps } from './defaultProps'; +import { SideBarProvider } from './SideBarContext'; +import useDefault from '../_util/useDefault'; +import parseTNode from '../_util/parseTNode'; +import useDefaultProps from '../hooks/useDefaultProps'; +import { usePrefixClass } from '../hooks/useClass'; + +export interface SideBarProps extends TdSideBarProps, StyledProps {} + +/** + * SideBar is a sidebar component that can be used to display a list of items in a collapsible menu. + * + * @param {Object} props - The properties of the SideBar component. + * @returns The rendered SideBar component. + */ +const SideBar = forwardRef((originProps, ref) => { + const props = useDefaultProps(originProps, sideBarDefaultProps); + const sideBarClass = usePrefixClass('side-bar'); + + const { onClick, onChange, children, defaultValue, value } = props; + const [activeValue, onToggleActiveValue] = useDefault(value, defaultValue, onChange); + + const defaultIndex = useRef(-1); + const updateChild = onToggleActiveValue; + + const [childrenList, setChildrenList] = useState([]); + + useEffect(() => { + onChange?.(activeValue); + }, [activeValue, onChange]); + + /** + * Adds a child component to the children array. + * + * @param {React.Element} child - The child component to add. + */ + const relation = useCallback((child) => { + setChildrenList((prevChildrenList) => [...prevChildrenList, child]); + }, []); + + /** + * Removes a child component from the children array. + * + * @param {React.Element} child - The child component to remove. + */ + const removeRelation = useCallback((child) => { + setChildrenList((prevChildrenList) => prevChildrenList.filter((item) => item !== child)); + }, []); + + /** + * Handles the click event on a SideBar item. + * + * @param {string | number} cur - The value of the clicked item. + * @param {string} label - The label of the clicked item. + */ + const onClickItem = useCallback( + (cur, label) => { + onToggleActiveValue(cur); + onClick?.(cur, label); + }, + [onToggleActiveValue, onClick], + ); + + const memoProviderValues = useMemo( + () => ({ + defaultIndex, + activeValue, + updateChild, + childrenList, + relation, + removeRelation, + onClickItem, + }), + [activeValue, updateChild, childrenList, relation, removeRelation, onClickItem], + ); + + return ( +
+ {parseTNode(children)} +
+
+ ); +}); + +export default memo(SideBar); diff --git a/src/side-bar/SideBarContext.tsx b/src/side-bar/SideBarContext.tsx new file mode 100644 index 00000000..2f201105 --- /dev/null +++ b/src/side-bar/SideBarContext.tsx @@ -0,0 +1,22 @@ +import React, { createContext, MutableRefObject, useMemo } from 'react'; +import { ChangeHandler } from '../_util/useDefault'; +import { TdSideBarProps } from './type'; + +/** + * SideBarContext is a React context that stores the state and actions related to the SideBar component. + */ +export const SideBarContext = createContext< + { + defaultIndex: MutableRefObject; + activeValue: number | string | (number | string)[]; + updateChild: ChangeHandler; + relation: (child: any) => void; + removeRelation: (child: any) => void; + onClickItem: (cur: any, label: any) => void; + } & Pick +>(null); + +export function SideBarProvider({ children, value }) { + const memoValue = useMemo(() => value, [value]); + return {children}; +} diff --git a/src/side-bar/SideBarItem.tsx b/src/side-bar/SideBarItem.tsx new file mode 100644 index 00000000..7c226f6d --- /dev/null +++ b/src/side-bar/SideBarItem.tsx @@ -0,0 +1,58 @@ +import React, { forwardRef, memo, useContext, useEffect } from 'react'; +import cls from 'classnames'; +import type { StyledProps } from '../common'; +import type { TdSideBarItemProps } from './type'; +import { SideBarContext } from './SideBarContext'; +import Badge from '../badge'; +import parseTNode from '../_util/parseTNode'; +import { sideBarItemDefaultProps } from './defaultProps'; +import useDefaultProps from '../hooks/useDefaultProps'; +import { usePrefixClass } from '../hooks/useClass'; + +export interface SideBarItemProps extends TdSideBarItemProps, StyledProps {} + +const SideBarItem = forwardRef((originProps, ref) => { + const props = useDefaultProps(originProps, sideBarItemDefaultProps); + const { badgeProps, disabled, icon, label, value } = props; + const { relation, removeRelation, activeValue, onClickItem } = useContext(SideBarContext); + + const sideBarItemClass = usePrefixClass('side-bar-item'); + + useEffect(() => { + relation(props); + return () => { + removeRelation(props); + }; + }, [props, relation, removeRelation]); + + const isActive = activeValue === value; + + const onClick = () => { + if (!disabled) onClickItem(value, label); + }; + + const isShowBadge = badgeProps?.count || badgeProps?.dot; + + return ( +
+ {isActive && ( +
+
+
+
+
+ )} + {icon &&
{parseTNode(icon)}
} + {isShowBadge ? :
{label}
} +
+ ); +}); + +export default memo(SideBarItem); diff --git a/src/side-bar/__tests__/__snapshots__/side-bar.test.tsx.snap b/src/side-bar/__tests__/__snapshots__/side-bar.test.tsx.snap new file mode 100644 index 00000000..dc66d572 --- /dev/null +++ b/src/side-bar/__tests__/__snapshots__/side-bar.test.tsx.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`SideBar 组件测试 > content 1`] = `null`; diff --git a/src/side-bar/__tests__/side-bar.test.tsx b/src/side-bar/__tests__/side-bar.test.tsx new file mode 100644 index 00000000..cf1c3aae --- /dev/null +++ b/src/side-bar/__tests__/side-bar.test.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { describe, it, expect, render } from '@test/utils'; + +import SideBar from '../SideBar'; + +describe('SideBar 组件测试', () => { + const SideBarText = 'SideBar组件'; + it('content', async () => { + const { queryByText } = render(); + expect(queryByText(SideBarText)).toMatchSnapshot(); + }); +}); diff --git a/src/side-bar/_example/base.tsx b/src/side-bar/_example/base.tsx new file mode 100644 index 00000000..b4904431 --- /dev/null +++ b/src/side-bar/_example/base.tsx @@ -0,0 +1,93 @@ +import React, { useState, useEffect, useRef, useCallback } from 'react'; +import { Grid, GridItem } from '../../grid'; +import { SideBarProps, SideBarItemProps, SideBar, SideBarItem } from '..'; +import './style/base.less'; + +const image = 'https://tdesign.gtimg.com/mobile/demos/example2.png'; +const items = new Array(12).fill({ label: '标题文字', image }); + +const categories = [ + { label: '选项一', title: '标题一', badgeProps: {}, items }, + { label: '选项二', title: '标题二', badgeProps: { dot: true }, items: items.slice(0, 9) }, + { label: '选项三', title: '标题三', badgeProps: {}, items: items.slice(0, 9) }, + { label: '选项四', title: '标题四', badgeProps: { count: 6 }, items: items.slice(0, 6) }, + { label: '选项五', title: '标题五', badgeProps: {}, items: items.slice(0, 3) }, +]; + +function SideBarWrapper() { + const [sideBarIndex, setSideBarIndex] = useState(1); + const [offsetTopList, setOffsetTopList] = useState([]); + const wrapperRef = useRef(null); + + const moveToActiveSideBar = useCallback( + (index) => { + if (wrapperRef.current) { + wrapperRef.current.scrollTop = offsetTopList[index] - offsetTopList[0]; + } + }, + [offsetTopList], + ); + + const onSideBarChange = (value) => { + setSideBarIndex(value); + moveToActiveSideBar(value); + }; + + const onScroll = (e) => { + const threshold = offsetTopList[0]; + const { scrollTop } = e.target; + if (scrollTop < threshold) { + setSideBarIndex(0); + return; + } + const index = offsetTopList.findIndex((top) => top > scrollTop && top - scrollTop <= threshold); + if (index > -1) { + setSideBarIndex(index); + } + }; + + const onSideBarClick = (value: SideBarProps['value'], label: SideBarItemProps['label']) => { + console.log('=onSideBarClick===', value, label); + }; + + useEffect(() => { + const newOffsetTopList = []; + const titles = wrapperRef.current.querySelectorAll('.title'); + titles.forEach((title) => { + newOffsetTopList.push(title.offsetTop); + }); + setOffsetTopList(newOffsetTopList); + moveToActiveSideBar(sideBarIndex); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [sideBarIndex]); + + return ( + <> +
+ + {categories.map((item, index) => ( + + ))} + +
+ {categories.map((item, index) => ( +
+
{item.title || item.label}
+ + {item.items.map((cargo, cargoIndex) => ( + + ))} + +
+ ))} +
+
+ + ); +} + +export default SideBarWrapper; diff --git a/src/side-bar/_example/custom.tsx b/src/side-bar/_example/custom.tsx new file mode 100644 index 00000000..992e5b1b --- /dev/null +++ b/src/side-bar/_example/custom.tsx @@ -0,0 +1,93 @@ +import React, { useState, useEffect, useRef, useCallback } from 'react'; +import { Grid, GridItem } from '../../grid'; +import { SideBarProps, SideBarItemProps, SideBar, SideBarItem } from '..'; +import './style/custom.less'; + +const image = 'https://tdesign.gtimg.com/mobile/demos/example2.png'; +const items = new Array(12).fill({ label: '标题文字', image }); + +const categories = [ + { label: '选项一', title: '标题一', badgeProps: {}, items }, + { label: '选项二', title: '标题二', badgeProps: { dot: true }, items: items.slice(0, 9) }, + { label: '选项三', title: '标题三', badgeProps: {}, items: items.slice(0, 9) }, + { label: '选项四', title: '标题四', badgeProps: { count: 6 }, items: items.slice(0, 6) }, + { label: '选项五', title: '标题五', badgeProps: {}, items: items.slice(0, 3) }, +]; + +function SideBarWrapper() { + const [sideBarIndex, setSideBarIndex] = useState(1); + const [offsetTopList, setOffsetTopList] = useState([]); + const wrapperRef = useRef(null); + + const moveToActiveSideBar = useCallback( + (index) => { + if (wrapperRef.current) { + wrapperRef.current.scrollTop = offsetTopList[index] - offsetTopList[0]; + } + }, + [offsetTopList], + ); + + const onSideBarChange = (value) => { + setSideBarIndex(value); + moveToActiveSideBar(value); + }; + + const onScroll = (e) => { + const threshold = offsetTopList[0]; + const { scrollTop } = e.target; + if (scrollTop < threshold) { + setSideBarIndex(0); + return; + } + const index = offsetTopList.findIndex((top) => top > scrollTop && top - scrollTop <= threshold); + if (index > -1) { + setSideBarIndex(index); + } + }; + + const onSideBarClick = (value: SideBarProps['value'], label: SideBarItemProps['label']) => { + console.log('=onSideBarClick===', value, label); + }; + + useEffect(() => { + const newOffsetTopList = []; + const titles = wrapperRef.current.querySelectorAll('.title'); + titles.forEach((title) => { + newOffsetTopList.push(title.offsetTop); + }); + setOffsetTopList(newOffsetTopList); + moveToActiveSideBar(sideBarIndex); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [sideBarIndex]); + + return ( + <> +
+ + {categories.map((item, index) => ( + + ))} + +
+ {categories.map((item, index) => ( +
+
{item.title || item.label}
+ + {item.items.map((cargo, cargoIndex) => ( + + ))} + +
+ ))} +
+
+ + ); +} + +export default SideBarWrapper; diff --git a/src/side-bar/_example/index.tsx b/src/side-bar/_example/index.tsx new file mode 100644 index 00000000..749ba950 --- /dev/null +++ b/src/side-bar/_example/index.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Button } from 'tdesign-mobile-react'; +import TDemoBlock from '../../../site/mobile/components/DemoBlock'; +import TDemoHeader from '../../../site/mobile/components/DemoHeader'; +import './style/index.less'; + +export default function Base() { + const navigate = useNavigate(); + const handleClick = (t = 'base') => { + navigate(`/side-bar-${t}?showNavBack=true`); + }; + return ( +
+ + + + + + + + + + + +
+ ); +} diff --git a/src/side-bar/_example/style/base.less b/src/side-bar/_example/style/base.less new file mode 100644 index 00000000..4babbb3e --- /dev/null +++ b/src/side-bar/_example/style/base.less @@ -0,0 +1,31 @@ +.side-bar-wrapper { + &.section-base { + display: flex; + height: calc(100vh - 50px); + background-color: var(--bg-color-demo, #fff); + z-index: 0; + .content { + flex: 1; + overflow-y: scroll; + } + + .section { + padding: 16px 0; + } + + .title { + padding-left: 20px; + margin-bottom: 4px; + line-height: 26px; + } + + .image { + width: 48px; + height: 48px; + } + } +} + +.tdesign-demo-topnav { + z-index: 1; +} diff --git a/src/side-bar/_example/style/custom.less b/src/side-bar/_example/style/custom.less new file mode 100644 index 00000000..ff3c984c --- /dev/null +++ b/src/side-bar/_example/style/custom.less @@ -0,0 +1,34 @@ +.side-bar-wrapper { + &.section-custom { + display: flex; + height: calc(100vh - 50px); + background-color: var(--bg-color-demo, #fff); + + --td-side-bar-border-radius: 6px; + --td-side-bar-active-color: green; + + .content { + flex: 1; + overflow-y: scroll; + } + + .section { + padding: 16px 0; + } + + .title { + padding-left: 20px; + margin-bottom: 4px; + line-height: 26px; + } + + .image { + width: 48px; + height: 48px; + } + } +} + +.tdesign-demo-topnav { + z-index: 1; +} diff --git a/src/side-bar/_example/style/index.less b/src/side-bar/_example/style/index.less new file mode 100644 index 00000000..a4e6c637 --- /dev/null +++ b/src/side-bar/_example/style/index.less @@ -0,0 +1,8 @@ +.tdesign-mobile-demo { + background-color: #ffffff; + padding-bottom: 28px; + + .t-button { + margin-bottom: 16px; + } +} diff --git a/src/side-bar/_example/style/switch.less b/src/side-bar/_example/style/switch.less new file mode 100644 index 00000000..c2bdac4b --- /dev/null +++ b/src/side-bar/_example/style/switch.less @@ -0,0 +1,35 @@ +.side-bar-wrapper { + &.section-switch { + display: flex; + height: calc(100vh - 50px); + background-color: var(--bg-color-demo, #fff); + overflow: hidden; + .content { + flex: 1; + transition: transform 0.3s ease; + } + + .section { + padding: 16px 0; + box-sizing: border-box; + height: 100%; + overflow-y: auto; + } + + .title { + padding-left: 20px; + margin-bottom: 4px; + line-height: 26px; + } + + .image { + width: 48px; + height: 48px; + border: 1px solid #e7e7e7; + } + } +} + +.tdesign-demo-topnav { + z-index: 1; +} diff --git a/src/side-bar/_example/switch.tsx b/src/side-bar/_example/switch.tsx new file mode 100644 index 00000000..00680c17 --- /dev/null +++ b/src/side-bar/_example/switch.tsx @@ -0,0 +1,55 @@ +import React, { useState, useRef } from 'react'; +import { Cell, Image } from 'tdesign-mobile-react'; +import { SideBarProps, SideBarItemProps, SideBar, SideBarItem } from '..'; +import './style/switch.less'; + +const image = 'https://tdesign.gtimg.com/mobile/demos/example2.png'; +const items = new Array(12).fill({ label: '标题文字', image }, 0, 12); + +const categories = [ + { label: '选项一', title: '标题一', badgeProps: {}, items }, + { label: '选项二', title: '标题二', badgeProps: { dot: true }, items: items.slice(0, 9) }, + { label: '选项三', title: '标题三', badgeProps: {}, items: items.slice(0, 9) }, + { label: '选项四', title: '标题四', badgeProps: { count: 6 }, items: items.slice(0, 6) }, + { label: '选项五', title: '标题五', badgeProps: {}, items: items.slice(0, 3) }, +]; + +function SideBarWrapper() { + const [sideBarIndex, setSideBarIndex] = useState(1); + const wrapperRef = useRef(null); + + const onSideBarChange = setSideBarIndex; + + const onSideBarClick = (value: SideBarProps['value'], label: SideBarItemProps['label']) => { + console.log('=onSideBarClick===', value, label); + }; + + return ( + <> +
+ + {categories.map((item, index) => ( + + ))} + + +
+ {categories.map((item, index) => ( +
+
{item.title || item.label}
+ {item.items.map((cargo, cargoIndex) => ( + } + /> + ))} +
+ ))} +
+
+ + ); +} + +export default SideBarWrapper; diff --git a/src/side-bar/_example/with-icon.tsx b/src/side-bar/_example/with-icon.tsx new file mode 100644 index 00000000..a23b6ce0 --- /dev/null +++ b/src/side-bar/_example/with-icon.tsx @@ -0,0 +1,95 @@ +import React, { useState, useEffect, useRef, useCallback } from 'react'; +import { Icon } from 'tdesign-icons-react'; +import { Grid, GridItem } from '../../grid'; +import { SideBarProps, SideBarItemProps, SideBar, SideBarItem } from '..'; +import './style/base.less'; + +const image = 'https://tdesign.gtimg.com/mobile/demos/example2.png'; +const items = new Array(12).fill({ label: '标题文字', image }); +const icon = ; + +const categories = [ + { label: '选项一', title: '标题一', badgeProps: {}, items, icon }, + { label: '选项二', title: '标题二', badgeProps: { dot: true }, items: items.slice(0, 9), icon }, + { label: '选项三', title: '标题三', badgeProps: {}, items: items.slice(0, 9), icon }, + { label: '选项四', title: '标题四', badgeProps: { count: 6 }, items: items.slice(0, 6), icon }, + { label: '选项五', title: '标题五', badgeProps: {}, items: items.slice(0, 3), icon }, +]; + +function SideBarWrapper() { + const [sideBarIndex, setSideBarIndex] = useState(1); + const [offsetTopList, setOffsetTopList] = useState([]); + const wrapperRef = useRef(null); + + const moveToActiveSideBar = useCallback( + (index) => { + if (wrapperRef.current) { + wrapperRef.current.scrollTop = offsetTopList[index] - offsetTopList[0]; + } + }, + [offsetTopList], + ); + + const onSideBarChange = (value) => { + setSideBarIndex(value); + moveToActiveSideBar(value); + }; + + const onScroll = (e) => { + const threshold = offsetTopList[0]; + const { scrollTop } = e.target; + if (scrollTop < threshold) { + setSideBarIndex(0); + return; + } + const index = offsetTopList.findIndex((top) => top > scrollTop && top - scrollTop <= threshold); + if (index > -1) { + setSideBarIndex(index); + } + }; + + const onSideBarClick = (value: SideBarProps['value'], label: SideBarItemProps['label']) => { + console.log('=onSideBarClick===', value, label); + }; + + useEffect(() => { + const newOffsetTopList = []; + const titles = wrapperRef.current.querySelectorAll('.title'); + titles.forEach((title) => { + newOffsetTopList.push(title.offsetTop); + }); + setOffsetTopList(newOffsetTopList); + moveToActiveSideBar(sideBarIndex); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [sideBarIndex]); + + return ( + <> +
+ + {categories.map((item, index) => ( + + ))} + +
+ {categories.map((item, index) => ( +
+
{item.title || item.label}
+ + {item.items.map((cargo, cargoIndex) => ( + + ))} + +
+ ))} +
+
+ + ); +} + +export default SideBarWrapper; diff --git a/src/side-bar/defaultProps.ts b/src/side-bar/defaultProps.ts new file mode 100644 index 00000000..2617290e --- /dev/null +++ b/src/side-bar/defaultProps.ts @@ -0,0 +1,9 @@ +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdSideBarProps, TdSideBarItemProps } from './type'; + +export const sideBarDefaultProps: TdSideBarProps = { defaultValue: undefined }; + +export const sideBarItemDefaultProps: TdSideBarItemProps = { disabled: false }; diff --git a/src/side-bar/index.ts b/src/side-bar/index.ts new file mode 100644 index 00000000..3537caf0 --- /dev/null +++ b/src/side-bar/index.ts @@ -0,0 +1,16 @@ +import _SideBar from './SideBar'; +import _SideBarItem from './SideBarItem'; + +import './style'; + +export type { SideBarProps } from './SideBar'; +export type { SideBarItemProps } from './SideBarItem'; +export * from './type'; + +export const SideBar = _SideBar; +export const SideBarItem = _SideBarItem; + +export default { + SideBar, + SideBarItem, +}; diff --git a/src/side-bar/side-bar.en-US.md b/src/side-bar/side-bar.en-US.md new file mode 100644 index 00000000..72733542 --- /dev/null +++ b/src/side-bar/side-bar.en-US.md @@ -0,0 +1,46 @@ +:: BASE_DOC :: + +## API + +### SideBar Props + +name | type | default | description | required +-- | -- | -- | -- | -- +className | String | - | className of component | N +style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N +children | TNode | - | children。Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +defaultValue | String / Number | undefined | \- | N +value | String / Number | - | \- | N +onChange | Function | | Typescript:`(value: number \| string) => void`
| N +onClick | Function | | Typescript:`(value: number \| string, label: string) => void`
| N + + +### SideBarItem Props + +name | type | default | description | required +-- | -- | -- | -- | -- +className | String | - | className of component | N +style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N +badgeProps | Object | - | Typescript:`BadgeProps`,[Badge API Documents](./badge?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/side-bar/type.ts) | N +disabled | Boolean | false | \- | N +icon | TElement | - | Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +label | String | - | \- | N +value | String / Number | - | \- | N + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-side-bar-active-color | @brand-color | - +--td-side-bar-bg-color | @bg-color-secondarycontainer | - +--td-side-bar-bg-color | @bg-color-secondarycontainer | - +--td-side-bar-border-radius | 9px | - +--td-side-bar-color | @font-gray-1 | - +--td-side-bar-disabled-color | @font-gray-4 | - +--td-side-bar-font-size | 16px | - +--td-side-bar-height | 100% | - +--td-side-bar-icon-size | 20px | - +--td-side-bar-item-height | 56px | - +--td-side-bar-item-line-height | 24px | - +--td-side-bar-width | 103px | - \ No newline at end of file diff --git a/src/side-bar/side-bar.md b/src/side-bar/side-bar.md new file mode 100644 index 00000000..e44a4c1f --- /dev/null +++ b/src/side-bar/side-bar.md @@ -0,0 +1,46 @@ +:: BASE_DOC :: + +## API + +### SideBar Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +className | String | - | 类名 | N +style | Object | - | 样式,TS 类型:`React.CSSProperties` | N +children | TNode | - | 侧边导航内容。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +defaultValue | String / Number | undefined | 选项值。非受控属性 | N +value | String / Number | - | 选项值 | N +onChange | Function | | TS 类型:`(value: number \| string) => void`
选项值发生变化时触发 | N +onClick | Function | | TS 类型:`(value: number \| string, label: string) => void`
点击选项时触发 | N + + +### SideBarItem Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +className | String | - | 类名 | N +style | Object | - | 样式,TS 类型:`React.CSSProperties` | N +badgeProps | Object | - | 透传至 Badge 组件。TS 类型:`BadgeProps`,[Badge API Documents](./badge?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/side-bar/type.ts) | N +disabled | Boolean | false | 是否禁用 | N +icon | TElement | - | 图标。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +label | String | - | 展示的标签 | N +value | String / Number | - | 当前选项的值 | N + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-side-bar-active-color | @brand-color | - +--td-side-bar-bg-color | @bg-color-secondarycontainer | - +--td-side-bar-bg-color | @bg-color-secondarycontainer | - +--td-side-bar-border-radius | 9px | - +--td-side-bar-color | @font-gray-1 | - +--td-side-bar-disabled-color | @font-gray-4 | - +--td-side-bar-font-size | 16px | - +--td-side-bar-height | 100% | - +--td-side-bar-icon-size | 20px | - +--td-side-bar-item-height | 56px | - +--td-side-bar-item-line-height | 24px | - +--td-side-bar-width | 103px | - \ No newline at end of file diff --git a/src/side-bar/style/css.js b/src/side-bar/style/css.js new file mode 100644 index 00000000..6a9a4b13 --- /dev/null +++ b/src/side-bar/style/css.js @@ -0,0 +1 @@ +import './index.css'; diff --git a/src/side-bar/style/index.js b/src/side-bar/style/index.js new file mode 100644 index 00000000..5536bf0b --- /dev/null +++ b/src/side-bar/style/index.js @@ -0,0 +1 @@ +import '../../_common/style/mobile/components/side-bar/v2/_index.less'; diff --git a/src/side-bar/type.ts b/src/side-bar/type.ts new file mode 100644 index 00000000..18267831 --- /dev/null +++ b/src/side-bar/type.ts @@ -0,0 +1,56 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { BadgeProps } from '../badge'; +import { TNode, TElement } from '../common'; + +export interface TdSideBarProps { + /** + * 侧边导航内容 + */ + children?: TNode; + /** + * 选项值。非受控属性 + */ + defaultValue?: string | number; + /** + * 选项值 + */ + value?: string | number; + /** + * 选项值发生变化时触发 + */ + onChange?: (value: number | string) => void; + /** + * 点击选项时触发 + */ + onClick?: (value: number | string, label: string) => void; +} + +export interface TdSideBarItemProps { + /** + * 透传至 Badge 组件 + */ + badgeProps?: BadgeProps; + /** + * 是否禁用 + * @default false + */ + disabled?: boolean; + /** + * 图标 + */ + icon?: TElement; + /** + * 展示的标签 + * @default '' + */ + label?: string; + /** + * 当前选项的值 + */ + value?: string | number; +} diff --git a/test/snap/__snapshots__/csr.test.jsx.snap b/test/snap/__snapshots__/csr.test.jsx.snap index 3da6f6d6..bcd20a55 100644 --- a/test/snap/__snapshots__/csr.test.jsx.snap +++ b/test/snap/__snapshots__/csr.test.jsx.snap @@ -24029,6 +24029,5462 @@ exports[`csr snapshot test > csr test src/search/_example/shape.tsx 1`] = `
`; +exports[`csr snapshot test > csr test src/side-bar/_example/base.tsx 1`] = ` +
+