Browse Source

Merge branch 'release' into main

linyan 2 years ago
parent
commit
c3dad7ff61
79 changed files with 353 additions and 215 deletions
  1. 14 0
      content/navigation/pagination/index-en-US.md
  2. 14 0
      content/navigation/pagination/index.md
  3. 12 0
      content/start/changelog/index-en-US.md
  4. 13 0
      content/start/changelog/index.md
  5. 1 1
      lerna.json
  6. 1 1
      packages/semi-animation-react/package.json
  7. 1 1
      packages/semi-animation-styled/package.json
  8. 1 1
      packages/semi-animation/package.json
  9. 1 1
      packages/semi-eslint-plugin/package.json
  10. 4 1
      packages/semi-foundation/datePicker/datePicker.scss
  11. 2 0
      packages/semi-foundation/datePicker/foundation.ts
  12. 1 0
      packages/semi-foundation/datePicker/inputFoundation.ts
  13. 2 2
      packages/semi-foundation/input/foundation.ts
  14. 1 1
      packages/semi-foundation/package.json
  15. 29 0
      packages/semi-foundation/pagination/pagination.scss
  16. 1 0
      packages/semi-foundation/pagination/variables.scss
  17. 1 1
      packages/semi-foundation/utils/getDataAttr.ts
  18. 1 1
      packages/semi-icons/package.json
  19. 1 1
      packages/semi-illustrations/package.json
  20. 1 1
      packages/semi-next/package.json
  21. 1 1
      packages/semi-rspack/package.json
  22. 1 1
      packages/semi-scss-compile/package.json
  23. 1 1
      packages/semi-theme-default/package.json
  24. 5 0
      packages/semi-ui/_base/baseComponent.tsx
  25. 1 0
      packages/semi-ui/anchor/index.tsx
  26. 3 1
      packages/semi-ui/autoComplete/index.tsx
  27. 2 2
      packages/semi-ui/banner/index.tsx
  28. 1 1
      packages/semi-ui/breadcrumb/index.tsx
  29. 1 1
      packages/semi-ui/breadcrumb/item.tsx
  30. 1 1
      packages/semi-ui/calendar/dayCalendar.tsx
  31. 1 1
      packages/semi-ui/calendar/monthCalendar.tsx
  32. 1 1
      packages/semi-ui/calendar/rangeCalendar.tsx
  33. 2 2
      packages/semi-ui/calendar/weekCalendar.tsx
  34. 6 5
      packages/semi-ui/carousel/index.tsx
  35. 1 0
      packages/semi-ui/cascader/index.tsx
  36. 1 0
      packages/semi-ui/checkbox/checkbox.tsx
  37. 1 0
      packages/semi-ui/checkbox/checkboxGroup.tsx
  38. 1 1
      packages/semi-ui/collapse/index.tsx
  39. 22 15
      packages/semi-ui/collapsible/index.tsx
  40. 2 1
      packages/semi-ui/datePicker/_story/datePicker.stories.jsx
  41. 17 0
      packages/semi-ui/datePicker/_story/v2/FeatInsetInputShowClear.tsx
  42. 1 0
      packages/semi-ui/datePicker/_story/v2/index.js
  43. 10 6
      packages/semi-ui/datePicker/dateInput.tsx
  44. 4 2
      packages/semi-ui/datePicker/datePicker.tsx
  45. 3 2
      packages/semi-ui/descriptions/index.tsx
  46. 4 3
      packages/semi-ui/descriptions/item.tsx
  47. 2 2
      packages/semi-ui/empty/index.tsx
  48. 7 10
      packages/semi-ui/input/index.tsx
  49. 3 2
      packages/semi-ui/list/index.tsx
  50. 4 1
      packages/semi-ui/list/item.tsx
  51. 3 1
      packages/semi-ui/modal/ModalContent.tsx
  52. 3 2
      packages/semi-ui/navigation/index.tsx
  53. 7 7
      packages/semi-ui/package.json
  54. 15 1
      packages/semi-ui/pagination/_story/pagination.stories.jsx
  55. 37 23
      packages/semi-ui/pagination/index.tsx
  56. 5 0
      packages/semi-ui/progress/index.tsx
  57. 3 1
      packages/semi-ui/radio/radio.tsx
  58. 2 0
      packages/semi-ui/radio/radioGroup.tsx
  59. 2 1
      packages/semi-ui/rating/index.tsx
  60. 2 2
      packages/semi-ui/scrollList/index.tsx
  61. 3 1
      packages/semi-ui/select/index.tsx
  62. 22 1
      packages/semi-ui/sideSheet/SideSheetContent.tsx
  63. 2 1
      packages/semi-ui/slider/index.tsx
  64. 2 2
      packages/semi-ui/spin/index.tsx
  65. 3 1
      packages/semi-ui/steps/basicSteps.tsx
  66. 3 1
      packages/semi-ui/steps/fillSteps.tsx
  67. 3 2
      packages/semi-ui/steps/navSteps.tsx
  68. 2 2
      packages/semi-ui/switch/index.tsx
  69. 3 0
      packages/semi-ui/table/Table.tsx
  70. 1 2
      packages/semi-ui/tabs/index.tsx
  71. 2 0
      packages/semi-ui/tagInput/index.tsx
  72. 3 2
      packages/semi-ui/timeline/index.tsx
  73. 3 1
      packages/semi-ui/timeline/item.tsx
  74. 2 2
      packages/semi-ui/transfer/index.tsx
  75. 2 1
      packages/semi-ui/tree/index.tsx
  76. 3 1
      packages/semi-ui/treeSelect/index.tsx
  77. 2 1
      packages/semi-ui/upload/index.tsx
  78. 1 1
      packages/semi-webpack/package.json
  79. 0 79
      yarn.lock

+ 14 - 0
content/navigation/pagination/index-en-US.md

@@ -34,6 +34,19 @@ import { Pagination } from '@douyinfe/semi-ui';
 );
 ```
 
+### disabled
+
+Disabled via the `disabled` setting
+
+```jsx live=true width=60%
+import React from 'react';
+import { Pagination } from '@douyinfe/semi-ui';
+
+() => (
+    <Pagination total={30} disabled style={{ marginBottom: 12 }}></Pagination>
+);
+```
+
 ### Show total page number
 
 Use the showTotal property to control whether the total number of pages is shown.
@@ -184,6 +197,7 @@ import { Pagination } from '@douyinfe/semi-ui';
 | currentPage        | Current page number                                                                                         | number                                          |                     |              |
 | defaultCurrentPage | Default current page number                                                                                 | number                                          |                     |              |
 | hideOnSinglePage   | Whether to hide the page divider automatically when the total number of pages is less than 2. When showSizeChanger is true, this switch no longer takes effect                | boolean                                         | false               |              |
+| disabled           | disabled                                                                             | boolean                                         |false                  | 2.37.0
 | hoverShowPageSelect  | Whether to show the page select when hover page (only work when size='small')                             | boolean                                         | false               |   1.27.0     |
 | nextText           | Text displayed by the next Page button                                                                      | string\| React Node                             |                     |              |
 | pageSize           | Number of entries per page                                                                                  | number                                          | 10                  |              |

+ 14 - 0
content/navigation/pagination/index.md

@@ -34,6 +34,19 @@ import { Pagination } from '@douyinfe/semi-ui';
 );
 ```
 
+### 禁用
+
+通过 `disabled` 设置禁用
+
+```jsx live=true width=60%
+import React from 'react';
+import { Pagination } from '@douyinfe/semi-ui';
+
+() => (
+    <Pagination total={30} disabled style={{ marginBottom: 12 }}></Pagination>
+);
+```
+
 ### 总页数显示
 
 通过 `showTotal` 属性控制是否展示总页数
@@ -177,6 +190,7 @@ import { Pagination } from '@douyinfe/semi-ui';
 | className          | 类名                                                                              | string                                          |                     |
 | currentPage        | 当前页码                                                                          | number                                          |                     |
 | defaultCurrentPage | 默认的当前页码                                                                    | number                                          |                     |
+| disabled           | 禁用                                                                             | boolean                                         |false                  | 2.37.0
 | hideOnSinglePage   | 总页数小于 2 时,是否自动隐藏分页器,当 showSizeChanger 为true时,此开关不再生效           | boolean                                            | false               |
 | hoverShowPageSelect  | hover 页码时是否展示切换页数的Select控件,仅当 size = 'small'时生效  | boolean             | false               | 1.27.0|
 | nextText           | 下一页文本                                                                        | string\|ReactNode                               |                     |

+ 12 - 0
content/start/changelog/index-en-US.md

@@ -16,6 +16,18 @@ Version:Major.Minor.Patch (follow the **Semver** specification)
 
 ---
 
+#### 🎉 2.37.0-beta.0 (2023-06-05)
+- 【Feat】
+    - Pagination support disabled API [#1641](https://github.com/DouyinFE/semi-design/pull/1641)
+    - DatePicker insetInput input box supports clearing input box content through trigger [#1638](https://github.com/DouyinFE/semi-design/issues/1638)
+    - Add the function of transparently passing data-* class attributes to all components [#1597](https://github.com/DouyinFE/semi-design/issues/1597)
+- 【Fix】
+    - DatePicker preset panel title supports i18n [#1643](https://github.com/DouyinFE/semi-design/pull/1643)
+    - Fix DatePicker insetInput input box placeholder placeholder text error [#1638](https://github.com/DouyinFE/semi-design/issues/1638)
+    - Fix DatePicker range input clear icon color bug [#1638](https://github.com/DouyinFE/semi-design/issues/1638)
+    - the stopPropagation and preventDefault of the up and down arrow press events are triggered only when the Dropdown panel is visible[#1640](https://github.com/DouyinFE/semi-design/pull/1640)
+- 【Style】
+    - Set the default line break rules for content in Tooltip, Toast, and Notification [#1623](https://github.com/DouyinFE/semi-design/pull/1623)
 
 #### 🎉 2.36.0 (2023-05-26)
 - 【Fix】

+ 13 - 0
content/start/changelog/index.md

@@ -13,6 +13,19 @@ Semi 版本号遵循 **Semver** 规范(主版本号-次版本号-修订版本
 -   修订版本号(patch):仅会进行 bugfix,发布时间不限
 -   不同版本间的详细关系,可查阅 [FAQ](/zh-CN/start/faq)
 
+#### 🎉 2.37.0-beta.0 (2023-06-05)
+- 【Feat】
+    - Pagination 新增 disabled API [#1641](https://github.com/DouyinFE/semi-design/pull/1641)
+    - DatePicker 内嵌输入框在 trigger 上增加 clear 按钮 [#1638](https://github.com/DouyinFE/semi-design/issues/1638)
+    - 为所有组件添加 data-* 类属性透传的功能 [#1597](https://github.com/DouyinFE/semi-design/issues/1597)
+- 【Fix】
+    - DatePicker preset 面板标题支持 i18n [#1643](https://github.com/DouyinFE/semi-design/pull/1643)
+    - 修复 DatePicker insetInput 内嵌输入框 placeholder 占位文本错误问题 [#1638](https://github.com/DouyinFE/semi-design/issues/1638)
+    - 修复 DatePicker 范围输入框 clear 按钮默认颜色不对问题 [#1638](https://github.com/DouyinFE/semi-design/issues/1638)
+    - 仅当 Dropdown panel 可见时,才触发上下箭头按下事件的 stopPropagation 和 preventDefault [#1640](https://github.com/DouyinFE/semi-design/pull/1640)
+- 【Style】
+    - 设置 Tooltip、Toast、Notification 中内容的默认换行换行规则 [#1623](https://github.com/DouyinFE/semi-design/pull/1623)
+
 #### 🎉 2.36.0 (2023-05-26)
 - 【Fix】
   - 修复 Calander 日历多日模式中,range包括时间时不显示非全天日程的问题 [@sylingd](https://github.com/sylingd)

+ 1 - 1
lerna.json

@@ -1,5 +1,5 @@
 {
     "useWorkspaces": true,
     "npmClient": "yarn",
-    "version": "2.36.0"
+    "version": "2.37.0-beta.0"
 }

+ 1 - 1
packages/semi-animation-react/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-animation-react",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "motion library for semi-ui-react",
     "keywords": [
         "motion",

+ 1 - 1
packages/semi-animation-styled/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-animation-styled",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "semi styled animation",
     "keywords": [
         "semi",

+ 1 - 1
packages/semi-animation/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-animation",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "animation base library for semi-ui",
     "keywords": [
         "animation",

+ 1 - 1
packages/semi-eslint-plugin/package.json

@@ -1,6 +1,6 @@
 {
     "name": "eslint-plugin-semi-design",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "semi ui eslint plugin",
     "keywords": [
         "semi",

+ 4 - 1
packages/semi-foundation/datePicker/datePicker.scss

@@ -993,13 +993,16 @@ $module-list: #{$prefix}-scrolllist;
             &:hover {
                 .#{$module}-range-input-clearbtn {
                     display: flex;
-                    color: $color-datepicker_range_input_clearbtn-icon-hover;
                     cursor: pointer;
                 }
 
                 .#{$module}-range-input-clearbtn ~ .#{$module}-range-input-suffix {
                     display: none;
                 }
+
+                .#{$module}-range-input-clearbtn:hover {
+                    color: $color-datepicker_range_input_clearbtn-icon-hover;
+                }
             }
 
             &-disabled {

+ 2 - 0
packages/semi-foundation/datePicker/foundation.ts

@@ -617,9 +617,11 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
         const inputValue = '';
         if (!this._isControlledComponent('value')) {
             this._updateValueAndInput(value, true, inputValue);
+            this._adapter.updateInsetInputValue(null);
             this.resetCachedSelectedValue(value);
         }
         this._notifyChange(value);
+        this._adapter.setRangeInputFocus(false);
         this._adapter.notifyClear(e);
     }
     // eslint-disable-next-line @typescript-eslint/no-empty-function

+ 1 - 0
packages/semi-foundation/datePicker/inputFoundation.ts

@@ -261,6 +261,7 @@ export default class InputFoundation extends BaseFoundation<DateInputAdapter> {
             case 'dateTime':
             case 'dateTimeRange':
                 [datePlaceholder, timePlaceholder] = insetInputFormat.split(' ');
+                break;
             case 'monthRange':
                 datePlaceholder = insetInputFormat + rangeSeparator + insetInputFormat;
                 break;

+ 2 - 2
packages/semi-foundation/input/foundation.ts

@@ -270,8 +270,8 @@ class InputFoundation extends BaseFoundation<InputAdapter> {
 
     isAllowClear() {
         const { value, isFocus, isHovering } = this._adapter.getStates();
-        const { showClear, disabled } = this._adapter.getProps();
-        const allowClear = value && showClear && !disabled && (isFocus || isHovering);
+        const { showClear, disabled, showClearIgnoreDisabled } = this._adapter.getProps();
+        const allowClear = value && showClear && (!disabled || showClearIgnoreDisabled) && (isFocus || isHovering);
         return allowClear;
     }
 

+ 1 - 1
packages/semi-foundation/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-foundation",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "",
     "scripts": {
         "build:lib": "node ./scripts/compileLib.js",

+ 29 - 0
packages/semi-foundation/pagination/pagination.scss

@@ -20,6 +20,14 @@ $module: #{$prefix}-page;
         padding: $spacing-pagination_small-paddingY $spacing-pagination_small-paddingX;
     }
 
+    &-disabled {
+        cursor: not-allowed;
+
+        .#{$module}-total {
+            color: $color-pagination_item-text-disabled;
+        }
+    }
+
     &-item {
         @include font-size-regular;
         min-width: $width-pagination_item-minWidth;
@@ -86,6 +94,27 @@ $module: #{$prefix}-page;
             min-width: $width-pagination_item_small-minWidth;
             margin: $spacing-pagination_item_small-margin;
         }
+
+        &-all-disabled {
+            border-color: $color-pagination_item-border-disabled;
+            color: $color-pagination_item-text-disabled;
+            background-color: $color-pagination_item-bg-disabled;
+            cursor: not-allowed;
+    
+            &:hover {
+                background-color: transparent;
+                color: $color-pagination_item-text-disabled;
+            }
+
+            &-active {
+                background-color: $color-pagination_item-bg-selected-disabled;
+                font-weight: $font-pagination_item_active-fontWeight;
+
+                &:hover {
+                    background-color: $color-pagination_item-bg-selected-disabled;
+                }
+            }
+        }
     }
 
     &-total {

+ 1 - 0
packages/semi-foundation/pagination/variables.scss

@@ -10,6 +10,7 @@ $color-pagination_item-bg-active: var(--semi-color-fill-1); // 翻页器 页码
 $color-pagination_item-text-disabled: var(--semi-color-disabled-text); // 翻页器 页码 禁用态文字颜色
 $color-pagination_item-icon-disabled: var(--semi-color-disabled-text); // 翻页器 页码 禁用态图标颜色
 $color-pagination_item-bg-disabled: transparent; // 翻页器 页码 禁用态背景颜色
+$color-pagination_item-bg-selected-disabled: var(--semi-color-disabled-fill); // 翻页器 页码 选中禁用态背景颜色
 $color-pagination_item-text-selected: var(--semi-color-primary); // 翻页器 页码 选中态文字颜色
 $color-pagination_item-bg-selected: var(--semi-color-primary-light-default); // 翻页器 页码 选中态背景颜色
 $color-pagination_quickjump_text-disabled: var(--semi-color-disabled-text); // 翻页器 快速跳转禁用态文字颜色

+ 1 - 1
packages/semi-foundation/utils/getDataAttr.ts

@@ -1,6 +1,6 @@
 export default function getDataAttr(props: Record<string, any>) {
     return Object.keys(props).reduce((prev, key) => {
-        if (key.substr(0, 5) === 'aria-' || key.substr(0, 5) === 'data-' || key === 'role') {
+        if (key.substr(0, 5) === 'data-') {
             prev[key] = props[key];
         }
         return prev;

+ 1 - 1
packages/semi-icons/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-icons",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "semi icons",
     "keywords": [
         "semi",

+ 1 - 1
packages/semi-illustrations/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-illustrations",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "semi illustrations",
     "keywords": [
         "semi",

+ 1 - 1
packages/semi-next/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-next",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "Plugin that support Semi Design in Next.js",
     "author": "伍浩威 <[email protected]>",
     "homepage": "",

+ 1 - 1
packages/semi-rspack/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-rspack-plugin",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "",
     "homepage": "",
     "license": "MIT",

+ 1 - 1
packages/semi-scss-compile/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-scss-compile",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "compile semi scss to css",
     "author": "[email protected]",
     "license": "MIT",

+ 1 - 1
packages/semi-theme-default/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-theme-default",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "semi-theme-default",
     "keywords": [
         "semi-theme",

+ 5 - 0
packages/semi-ui/_base/baseComponent.tsx

@@ -6,6 +6,7 @@ import React, { Component, ReactNode } from 'react';
 import log from '@douyinfe/semi-foundation/utils/log';
 import { DefaultAdapter } from '@douyinfe/semi-foundation/base/foundation';
 import { VALIDATE_STATUS } from '@douyinfe/semi-foundation/base/constants';
+import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 import { ArrayElement } from './base';
 
 const { hasOwnProperty } = Object.prototype;
@@ -79,4 +80,8 @@ export default class BaseComponent<P extends BaseProps = {}, S = {}> extends Com
     log(text: string, ...rest: any): any {
         return log(text, ...rest);
     }
+
+    getDataAttr(props?: any) {
+        return getDataAttr(props);
+    }
 }

+ 1 - 0
packages/semi-ui/anchor/index.tsx

@@ -317,6 +317,7 @@ class Anchor extends BaseComponent<AnchorProps, AnchorState> {
                     className={wrapperCls}
                     style={wrapperStyle}
                     id={this.anchorID}
+                    {...this.getDataAttr(this.props)}
                 >
                     <div aria-hidden className={slideCls} style={{ height: scrollHeight }}>
                         <span className={slideBarCls} style={{ top: slideBarTop }} />

+ 3 - 1
packages/semi-ui/autoComplete/index.tsx

@@ -7,6 +7,7 @@ import { isEqual, noop } from 'lodash';
 import { strings, cssClasses } from '@douyinfe/semi-foundation/autoComplete/constants';
 import AutoCompleteFoundation, { AutoCompleteAdapter, StateOptionItem, DataItem } from '@douyinfe/semi-foundation/autoComplete/foundation';
 import { numbers as popoverNumbers } from '@douyinfe/semi-foundation/popover/constants';
+import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 import BaseComponent, { ValidateStatus } from '../_base/baseComponent';
 import { Position } from '../tooltip';
 import Spin from '../spin';
@@ -370,7 +371,8 @@ class AutoComplete<T extends AutoCompleteItems> extends BaseComponent<AutoComple
             id,
             ...keyboardEventSet,
             // tooltip give tabindex 0 to children by default, autoComplete just need the input get focus, so outer div's tabindex set to -1
-            tabIndex: -1
+            tabIndex: -1,
+            ...this.getDataAttr(this.props)
         };
 
         const innerProps = {

+ 2 - 2
packages/semi-ui/banner/index.tsx

@@ -28,7 +28,7 @@ export interface BannerProps {
     closeIcon?: React.ReactNode;
     style?: React.CSSProperties;
     bordered?: boolean;
-    onClose?(e: React.MouseEvent):void
+    onClose?(e: React.MouseEvent): void
 }
 
 export interface BannerState {
@@ -155,7 +155,7 @@ export default class Banner extends BaseComponent<BannerProps, BannerState> {
             [`${prefixCls}-bordered`]: !fullMode && bordered,
         });
         const banner = visible ? (
-            <div className={wrapper} style={style} role="alert">
+            <div className={wrapper} style={style} role="alert" {...this.getDataAttr(this.props)}>
                 <div className={`${prefixCls}-content-wrapper`}>
                     <div className={`${prefixCls}-content`}>
                         {this.renderIcon()}

+ 1 - 1
packages/semi-ui/breadcrumb/index.tsx

@@ -291,7 +291,7 @@ class Breadcrumb extends BaseComponent<BreadcrumbProps, BreadcrumbState> {
                     separator,
                 }}
             >
-                <nav aria-label={this.props['aria-label']} className={sizeCls} style={style}>
+                <nav aria-label={this.props['aria-label']} className={sizeCls} style={style} {...this.getDataAttr(this.props)}>
                     {breadcrumbs}
                 </nav>
             </BreadContext.Provider>

+ 1 - 1
packages/semi-ui/breadcrumb/item.tsx

@@ -207,7 +207,7 @@ export default class BreadcrumbItem extends BaseComponent<BreadcrumbItemProps, B
             // [`${clsPrefix}-item-wrap-iconOnly`]: !!children && this.props.icon,
         });
         return (
-            <span className={wrapperCLs} {...pageLabel}>
+            <span className={wrapperCLs} {...pageLabel} {...this.getDataAttr(this.props)}>
                 {item}
                 {shouldRenderSeparator && separator}
             </span>

+ 1 - 1
packages/semi-ui/calendar/dayCalendar.tsx

@@ -166,7 +166,7 @@ export default class DayCalendar extends BaseComponent<DayCalendarProps, DayCale
         const { parsedEvents, scrollHeight } = this.state;
         this.isWeekend = markWeekend && this.checkWeekend(displayValue);
         return (
-            <div className={dayCls} style={dayStyle} ref={this.dom}>
+            <div className={dayCls} style={dayStyle} ref={this.dom} {...this.getDataAttr(this.props)}>
                 <div className={`${prefixCls}-sticky-top`}>
                     {header}
                     {this.renderAllDay(parsedEvents.allDay)}

+ 1 - 1
packages/semi-ui/calendar/monthCalendar.tsx

@@ -388,7 +388,7 @@ export default class monthCalendar extends BaseComponent<MonthCalendarProps, Mon
         return (
             <LocaleConsumer componentName="Calendar">
                 {(locale: Locale['Calendar'], localeCode: string, dateFnsLocale: Locale['dateFnsLocale']) => (
-                    <div role="grid" className={monthCls} key={this.state.itemLimit} style={monthStyle}>
+                    <div role="grid" className={monthCls} key={this.state.itemLimit} style={monthStyle} {...this.getDataAttr(this.props)}>
                         <div role="presentation" className={`${prefixCls}-sticky-top`}>
                             {header}
                             {this.renderHeader(dateFnsLocale)}

+ 1 - 1
packages/semi-ui/calendar/rangeCalendar.tsx

@@ -259,7 +259,7 @@ export default class RangeCalendar extends BaseComponent<RangeCalendarProps, Ran
         return (
             <LocaleConsumer componentName="Calendar">
                 {(locale: Locale['Calendar'], localeCode: string, dateFnsLocale: Locale['dateFnsLocale']) => (
-                    <div className={weekCls} style={weekStyle} ref={this.dom}>
+                    <div className={weekCls} style={weekStyle} ref={this.dom} {...this.getDataAttr(this.props)}>
                         <div className={`${prefixCls}-sticky-top`}>
                             {header}
                             {this.renderHeader(dateFnsLocale)}

+ 2 - 2
packages/semi-ui/calendar/weekCalendar.tsx

@@ -10,7 +10,7 @@ import DayCol from './dayCol';
 import TimeCol from './timeCol';
 import { isEqual } from 'lodash';
 import { calcRowHeight } from '@douyinfe/semi-foundation/calendar/eventUtil';
-import { WeekCalendarProps } from './interface';
+import type { WeekCalendarProps } from './interface';
 
 import '@douyinfe/semi-foundation/calendar/calendar.scss';
 import { Locale } from '../locale/interface';
@@ -260,7 +260,7 @@ export default class WeekCalendar extends BaseComponent<WeekCalendarProps, WeekC
         return (
             <LocaleConsumer componentName="Calendar">
                 {(locale: Locale['Calendar'], localeCode: string, dateFnsLocale: any) => (
-                    <div className={weekCls} style={weekStyle} ref={this.dom}>
+                    <div className={weekCls} style={weekStyle} ref={this.dom} {...this.getDataAttr(this.props)}>
                         <div className={`${prefixCls}-sticky-top`}>
                             {header}
                             {this.renderHeader(dateFnsLocale)}

+ 6 - 5
packages/semi-ui/carousel/index.tsx

@@ -150,21 +150,21 @@ class Carousel extends BaseComponent<CarouselProps, CarouselState> {
     };
     
     handleAutoPlay = (): void => {
-        if (!this.foundation.getIsControlledComponent()){
+        if (!this.foundation.getIsControlledComponent()) {
             this.foundation.handleAutoPlay();
         }
     }
 
     handleMouseEnter = (): void => {
         const { autoPlay } = this.props;
-        if ((autoPlay === true) || (typeof autoPlay === 'object' && autoPlay.hoverToPause)){
+        if ((autoPlay === true) || (typeof autoPlay === 'object' && autoPlay.hoverToPause)) {
             this.foundation.stop();
         }
     }
 
     handleMouseLeave = (): void => {
         const { autoPlay } = this.props;
-        if ((typeof autoPlay !== 'object' || autoPlay.hoverToPause) && !this.foundation.getIsControlledComponent()){
+        if ((typeof autoPlay !== 'object' || autoPlay.hoverToPause) && !this.foundation.getIsControlledComponent()) {
             this.foundation.handleAutoPlay();
         }
     }
@@ -231,7 +231,7 @@ class Carousel extends BaseComponent<CarouselProps, CarouselState> {
             [cssClasses.CAROUSEL_INDICATOR]: true
         });
 
-        if (showIndicator && children.length > 1){
+        if (showIndicator && children.length > 1) {
             return (
                 <div className={carouselIndicatorCls}>
                     <CarouselIndicator
@@ -254,7 +254,7 @@ class Carousel extends BaseComponent<CarouselProps, CarouselState> {
         const { children } = this.state;
         const { showArrow, arrowType, theme, arrowProps } = this.props;
 
-        if (showArrow && children.length > 1){
+        if (showArrow && children.length > 1) {
             return (
                 <CarouselArrow 
                     type={arrowType} 
@@ -285,6 +285,7 @@ class Carousel extends BaseComponent<CarouselProps, CarouselState> {
                 style={style} 
                 onMouseEnter={debounce(this.handleMouseEnter, 400)}
                 onMouseLeave={debounce(this.handleMouseLeave, 400)}
+                {...this.getDataAttr(this.props)}
                 // onMouseEnter={this.handleMouseEnter}
                 // onMouseLeave={this.handleMouseLeave}
                 // onKeyDown={e => this.foundation.handleKeyDown(e)}

+ 1 - 0
packages/semi-ui/cascader/index.tsx

@@ -998,6 +998,7 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
                 // eslint-disable-next-line jsx-a11y/role-has-required-aria-props
                 role="combobox"
                 tabIndex={0}
+                {...this.getDataAttr(this.props)}
             >
                 {inner}
             </div>

+ 1 - 0
packages/semi-ui/checkbox/checkbox.tsx

@@ -300,6 +300,7 @@ class Checkbox extends BaseComponent<CheckboxProps, CheckboxState> {
                 onClick={this.handleChange}
                 onKeyPress={this.handleEnterPress}
                 aria-labelledby={this.props['aria-labelledby']}
+                {...this.getDataAttr(this.props)}
             >
                 <CheckboxInner
                     {...this.props}

+ 1 - 0
packages/semi-ui/checkbox/checkboxGroup.tsx

@@ -172,6 +172,7 @@ class CheckboxGroup extends BaseComponent<CheckboxGroupProps, CheckboxGroupState
                 style={style}
                 aria-labelledby={this.props['aria-labelledby']}
                 aria-describedby={this.props['aria-describedby']}
+                {...this.getDataAttr(this.props)}
                 // aria-errormessage={this.props['aria-errormessage']}
                 // aria-invalid={this.props['aria-invalid']}
                 // aria-required={this.props['aria-required']}

+ 1 - 1
packages/semi-ui/collapse/index.tsx

@@ -100,7 +100,7 @@ class Collapse extends BaseComponent<CollapseReactProps, CollapseState> {
         const clsPrefix = cls(cssClasses.PREFIX, className);
         const { activeSet } = this.state;
         return (
-            <div className={clsPrefix} style={style}>
+            <div className={clsPrefix} style={style} {...this.getDataAttr(this.props)}>
                 <CollapseContext.Provider
                     value={{
                         activeSet,

+ 22 - 15
packages/semi-ui/collapsible/index.tsx

@@ -170,24 +170,31 @@ class Collapsible extends BaseComponent<CollapsibleProps, CollapsibleState> {
         const wrapperCls = cls(`${cssClasses.PREFIX}-wrapper`, {
             [`${cssClasses.PREFIX}-transition`]: this.props.motion && this.state.isTransitioning
         }, this.props.className);
-        return <div className={wrapperCls} style={wrapperStyle} onTransitionEnd={() => {
-            if (!this.props.isOpen) {
-                this.foundation.updateVisible(false);
-            }
-            this.foundation.updateIsTransitioning(false);
-            this.props.onMotionEnd?.();
-        }}>
+        return (
             <div
-                x-semi-prop="children"
-                ref={this.domRef}
-                style={{ overflow: 'hidden' }}
-                id={this.props.id}
+                className={wrapperCls}
+                style={wrapperStyle}
+                onTransitionEnd={() => {
+                    if (!this.props.isOpen) {
+                        this.foundation.updateVisible(false);
+                    }
+                    this.foundation.updateIsTransitioning(false);
+                    this.props.onMotionEnd?.();
+                }}
+                {...this.getDataAttr(this.props)}
             >
-                {
-                    (this.props.keepDOM || this.props.collapseHeight !== 0 || this.state.visible || this.props.isOpen) && this.props.children
-                }
+                <div
+                    x-semi-prop="children"
+                    ref={this.domRef}
+                    style={{ overflow: 'hidden' }}
+                    id={this.props.id}
+                >
+                    {
+                        (this.props.keepDOM || this.props.collapseHeight !== 0 || this.state.visible || this.props.isOpen) && this.props.children
+                    }
+                </div>
             </div>
-        </div>;
+        );
     }
 }
 

+ 2 - 1
packages/semi-ui/datePicker/_story/datePicker.stories.jsx

@@ -67,7 +67,8 @@ export {
     FixNeedConfirmInTabs,
     DynamicDisabledDate,
     FeatEtcGMT,
-    FixDisabledDate
+    FixDisabledDate,
+    FeatInsetInputShowClear
 } from './v2';
 
 

+ 17 - 0
packages/semi-ui/datePicker/_story/v2/FeatInsetInputShowClear.tsx

@@ -0,0 +1,17 @@
+import React from "react";
+import { DatePicker, Space, Input } from "@douyinfe/semi-ui";
+
+export default function App() {
+    const props = {
+        insetInput: true,
+        showClear: true,
+        position: "topLeft"
+    } as const;
+    return (
+        <Space vertical align="start">
+            <DatePicker {...props} />
+            <DatePicker {...props} type="dateRange" />
+            <DatePicker {...props} type="dateTimeRange" />
+        </Space>
+    );
+}

+ 1 - 0
packages/semi-ui/datePicker/_story/v2/index.js

@@ -25,3 +25,4 @@ export { default as DynamicDisabledDate } from './dynamicDisabledDate';
 export { default as FeatEtcGMT } from './FeatEtcGMT';
 export { default as FixDisabledDate } from './FixDisabledDate';
 export { default as FeatYearScrollRange } from './FeatYearScrollRange';
+export { default as FeatInsetInputShowClear } from './FeatInsetInputShowClear';

+ 10 - 6
packages/semi-ui/datePicker/dateInput.tsx

@@ -38,7 +38,8 @@ export interface DateInputProps extends DateInputFoundationProps, BaseProps {
     value?: Date[];
     inputRef?: React.RefObject<HTMLInputElement>;
     rangeInputStartRef?: React.RefObject<HTMLInputElement>;
-    rangeInputEndRef?: React.RefObject<HTMLInputElement>
+    rangeInputEndRef?: React.RefObject<HTMLInputElement>;
+    showClearIgnoreDisabled?: boolean
 }
 
 // eslint-disable-next-line @typescript-eslint/ban-types
@@ -200,15 +201,16 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
     }
 
     renderRangeClearBtn(rangeStart: string, rangeEnd: string) {
-        const { showClear, prefixCls, disabled, clearIcon } = this.props;
-        const allowClear = (rangeStart || rangeEnd) && showClear;
-        return allowClear && !disabled ? (
+        const { showClear, prefixCls, disabled, clearIcon, showClearIgnoreDisabled } = this.props;
+        const isRealDisabled = disabled && !showClearIgnoreDisabled;
+        const allowClear = (rangeStart || rangeEnd) && showClear && !isRealDisabled;
+        return allowClear ? (
             <div
                 role="button"
                 tabIndex={0}
                 aria-label="Clear range input value"
                 className={`${prefixCls}-range-input-clearbtn`}
-                onMouseDown={e => !disabled && this.handleRangeInputClear(e)}>
+                onMouseDown={e => this.handleRangeInputClear(e)}>
                 {clearIcon ? clearIcon : <IconClear aria-hidden />}
             </div>
         ) : null;
@@ -360,7 +362,7 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
                     onChange={this.handleInsetInputChange}
                     onFocus={handleInsetTimeFocus}
                 />
-                { this.isRenderMultipleInputs() && (
+                {this.isRenderMultipleInputs() && (
                     <>
                         <div className={separatorCls}>{density === 'compact' ? null : '-'}</div>
                         <InsetDateInput
@@ -420,6 +422,7 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
             insetInput,
             insetInputValue,
             defaultPickerValue,
+            showClearIgnoreDisabled,
             ...rest
         } = this.props;
         const dateIcon = <IconCalendar aria-hidden />;
@@ -448,6 +451,7 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
                 ref={inputRef}
                 insetLabel={insetLabel}
                 disabled={disabled}
+                showClearIgnoreDisabled={showClearIgnoreDisabled}
                 readonly={inputReadOnly}
                 className={inputCls}
                 style={inputStyle}

+ 4 - 2
packages/semi-ui/datePicker/datePicker.tsx

@@ -585,7 +585,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
             defaultPickerValue
         };
 
-        return insetInput ? <DateInput {...props} insetInput={insetInput}/> : null;
+        return insetInput ? <DateInput {...props} insetInput={insetInput} /> : null;
     }
 
     handleOpenPanel = () => this.foundation.openPanel();
@@ -667,6 +667,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
         // These values should be passed to triggerRender, do not delete any key if it is not necessary
         const props = {
             ...extraProps,
+            showClearIgnoreDisabled: Boolean(insetInput),
             placeholder: phText,
             clearIcon,
             disabled: inputDisabled,
@@ -863,7 +864,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
     };
 
     render() {
-        const { style, className, prefixCls, type } = this.props;
+        const { style, className, prefixCls, type, ...rest } = this.props;
         const outerProps = {
             style,
             className: classnames(className, { [prefixCls]: true }),
@@ -873,6 +874,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
             'aria-labelledby': this.props['aria-labelledby'],
             'aria-describedby': this.props['aria-describedby'],
             'aria-required': this.props['aria-required'],
+            ...this.getDataAttr(rest)
         };
 
         const innerPropKeys: string[] = [];

+ 3 - 2
packages/semi-ui/descriptions/index.tsx

@@ -3,6 +3,7 @@ import cls from 'classnames';
 import PropTypes from 'prop-types';
 import { strings, cssClasses } from '@douyinfe/semi-foundation/descriptions/constants';
 import '@douyinfe/semi-foundation/descriptions/descriptions.scss';
+import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 import { isPlainObject } from 'lodash';
 import DescriptionsContext, { DescriptionsAlign, DescriptionsContextValue } from './descriptions-context';
 import Item from './item';
@@ -54,7 +55,7 @@ class Descriptions extends PureComponent<DescriptionsProps> {
     };
 
     render() {
-        const { align, row, size, className, style, children, data } = this.props;
+        const { align, row, size, className, style, children, data, ...rest } = this.props;
         const classNames = cls(prefixCls, className, {
             [`${prefixCls}-${align}`]: !row,
             [`${prefixCls}-double`]: row,
@@ -66,7 +67,7 @@ class Descriptions extends PureComponent<DescriptionsProps> {
             )) :
             children;
         return (
-            <div className={classNames} style={style}>
+            <div className={classNames} style={style} {...getDataAttr(rest)}>
                 <table>
                     <tbody>
                         <DescriptionsContext.Provider value={{ align }}>

+ 4 - 3
packages/semi-ui/descriptions/item.tsx

@@ -2,6 +2,7 @@ import React, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { cssClasses } from '@douyinfe/semi-foundation/descriptions/constants';
 import '@douyinfe/semi-foundation/descriptions/descriptions.scss';
+import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 import DescriptionsContext, { DescriptionsContextValue } from './descriptions-context';
 
 export interface DescriptionsItemProps {
@@ -29,14 +30,14 @@ export default class Item extends PureComponent<DescriptionsItemProps> {
     context: DescriptionsContextValue;
 
     render() {
-        const { itemKey, hidden, className, style, children } = this.props;
+        const { itemKey, hidden, className, style, children, ...rest } = this.props;
         const { align } = this.context;
         if (hidden) {
             return null;
         }
         const item = align === 'plain' ?
             (
-                <tr className={className} style={style}>
+                <tr className={className} style={style} {...getDataAttr(rest)}>
                     <td className={`${prefixCls}-item`}>
                         <span className={keyCls}>
                             {itemKey}:
@@ -48,7 +49,7 @@ export default class Item extends PureComponent<DescriptionsItemProps> {
                 </tr>
             ) :
             (
-                <tr className={className} style={style}>
+                <tr className={className} style={style} {...getDataAttr(rest)}>
                     <th className={`${prefixCls}-item ${prefixCls}-item-th`}>
                         <span className={keyCls}>
                             {itemKey}

+ 2 - 2
packages/semi-ui/empty/index.tsx

@@ -76,7 +76,7 @@ export default class Empty extends BaseComponent<EmptyProps, EmptyState> {
     }
 
     render(): JSX.Element {
-        const { className, image, description, style, title, imageStyle, children, layout, darkModeImage } = this.props;
+        const { className, image, description, style, title, imageStyle, children, layout, darkModeImage, ...rest } = this.props;
 
         const alt = typeof description === 'string' ? description : 'empty';
         const imgSrc = ((this.state.mode === 'dark') && darkModeImage) ? darkModeImage : image;
@@ -108,7 +108,7 @@ export default class Empty extends BaseComponent<EmptyProps, EmptyState> {
                 style: { fontWeight: 400 },
             };
         return (
-            <div className={wrapperCls} style={style}>
+            <div className={wrapperCls} style={style} {...this.getDataAttr(rest)}>
                 <div className={`${prefixCls}-image`} style={imageStyle} x-semi-prop="image,darkModeImage">
                     {imageNode}
                 </div>

+ 7 - 10
packages/semi-ui/input/index.tsx

@@ -66,7 +66,9 @@ export interface InputProps extends
     inputStyle?: React.CSSProperties;
     getValueLength?: (value: string) => number;
     forwardRef?: ((instance: any) => void) | React.MutableRefObject<any> | null;
-    preventScroll?: boolean
+    preventScroll?: boolean;
+    /** internal prop, DatePicker use it */
+    showClearIgnoreDisabled?: boolean
 }
 
 export interface InputState {
@@ -319,7 +321,7 @@ class Input extends BaseComponent<InputProps, InputState> {
                     className={clearCls}
                     onMouseDown={this.handleClear}
                 >
-                    { clearIcon ? clearIcon : <IconClear />}
+                    {clearIcon ? clearIcon : <IconClear />}
                 </div>
             );
         }
@@ -380,12 +382,6 @@ class Input extends BaseComponent<InputProps, InputState> {
         );
     }
 
-    showClearBtn() {
-        const { value, isFocus, isHovering } = this.state;
-        const { disabled, showClear } = this.props;
-        return Boolean(value) && showClear && !disabled && (isFocus || isHovering);
-    }
-
     renderSuffix(suffixAllowClear: boolean) {
         const { suffix, hideSuffix } = this.props;
         if (!suffix) {
@@ -416,7 +412,7 @@ class Input extends BaseComponent<InputProps, InputState> {
             if (typeof forwardRef === 'function') {
                 return (node: HTMLInputElement) => {
                     forwardRef(node);
-                    this.inputRef = { current: node } ;
+                    this.inputRef = { current: node };
                 };
             } else if (Object.prototype.toString.call(forwardRef) === '[object Object]') {
                 this.inputRef = forwardRef;
@@ -456,10 +452,11 @@ class Input extends BaseComponent<InputProps, InputState> {
             getValueLength,
             preventScroll,
             borderless,
+            showClearIgnoreDisabled,
             ...rest
         } = this.props;
         const { value, isFocus, minLength: stateMinLength } = this.state;
-        const suffixAllowClear = this.showClearBtn();
+        const suffixAllowClear = this.foundation.isAllowClear();
         const suffixIsIcon = isSemiIcon(suffix);
         const ref = this.getInputRef();
         const wrapperPrefix = `${prefixCls}-wrapper`;

+ 3 - 2
packages/semi-ui/list/index.tsx

@@ -130,7 +130,8 @@ class List<T = any> extends BaseComponent<ListProps<T>> {
             bordered,
             dataSource,
             renderItem,
-            children
+            children,
+            ...rest
         } = this.props;
         const wrapperCls = cls(prefixCls, className, {
             [`${prefixCls}-flex`]: layout === 'horizontal',
@@ -155,7 +156,7 @@ class List<T = any> extends BaseComponent<ListProps<T>> {
             childrenList = this.renderEmpty();
         }
         return (
-            <div className={wrapperCls} style={style}>
+            <div className={wrapperCls} style={style} {...this.getDataAttr(rest)}>
                 {header ? (
                     <div className={`${cssClasses.PREFIX}-header`} x-semi-prop="header">
                         {header}

+ 4 - 1
packages/semi-ui/list/item.tsx

@@ -2,6 +2,7 @@ import React, { PureComponent } from 'react';
 import cls from 'classnames';
 import PropTypes from 'prop-types';
 import { cssClasses, strings } from '@douyinfe/semi-foundation/list/constants';
+import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 import { noop } from 'lodash';
 import { Col } from '../grid';
 import ListContext, { ListContextValue } from './list-context';
@@ -68,7 +69,8 @@ export default class ListItem extends PureComponent<ListItemProps> {
             onClick,
             onRightClick,
             onMouseEnter,
-            onMouseLeave
+            onMouseLeave,
+            ...rest
         } = this.props;
         const { onRightClick: contextOnRightClick, onClick: contextOnClick, grid: contextGrid } = this.context;
         const handleContextMenu = onRightClick ? onRightClick : contextOnRightClick;
@@ -97,6 +99,7 @@ export default class ListItem extends PureComponent<ListItemProps> {
                 onContextMenu={handleContextMenu}
                 onMouseEnter={onMouseEnter}
                 onMouseLeave={onMouseLeave}
+                {...getDataAttr(rest)}
             >
                 {body ? body : null}
                 {children}

+ 3 - 1
packages/semi-ui/modal/ModalContent.tsx

@@ -306,6 +306,7 @@ export default class ModalContent extends BaseComponent<ModalContentReactProps,
             getPopupContainer,
             maskFixed,
             getContainerContext,
+            ...rest
         } = this.props;
         const { direction } = this.context;
         const classList = cls(className, {
@@ -315,9 +316,10 @@ export default class ModalContent extends BaseComponent<ModalContentReactProps,
         });
 
         const containerContext = getContainerContext();
+        const dataAttr = this.getDataAttr(rest);
 
         const elem = (
-            <div className={classList}>
+            <div className={classList} {...dataAttr}>
                 {this.getMaskElement()}
                 <div
                     role="none"

+ 3 - 2
packages/semi-ui/navigation/index.tsx

@@ -314,7 +314,8 @@ class Nav extends BaseComponent<NavProps, NavState> {
             toggleIconPosition,
             limitIndent,
             renderWrapper,
-            getPopupContainer
+            getPopupContainer,
+            ...rest
         } = this.props;
 
         const { selectedKeys, openKeys, items, isCollapsed } = this.state;
@@ -417,7 +418,7 @@ class Nav extends BaseComponent<NavProps, NavState> {
                             getPopupContainer
                         } as any}
                     >
-                        <div className={finalCls} style={finalStyle}>
+                        <div className={finalCls} style={finalStyle} {...this.getDataAttr(rest)}>
                             <div className={`${prefixCls}-inner`}>
                                 <div className={headerListOuterCls}>
                                     {headers}

+ 7 - 7
packages/semi-ui/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-ui",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "",
     "main": "lib/cjs/index.js",
     "module": "lib/es/index.js",
@@ -17,12 +17,12 @@
         "lib/*"
     ],
     "dependencies": {
-        "@douyinfe/semi-animation": "2.36.0",
-        "@douyinfe/semi-animation-react": "2.36.0",
-        "@douyinfe/semi-foundation": "2.36.0",
-        "@douyinfe/semi-icons": "2.36.0",
-        "@douyinfe/semi-illustrations": "2.36.0",
-        "@douyinfe/semi-theme-default": "2.36.0",
+        "@douyinfe/semi-animation": "2.37.0-beta.0",
+        "@douyinfe/semi-animation-react": "2.37.0-beta.0",
+        "@douyinfe/semi-foundation": "2.37.0-beta.0",
+        "@douyinfe/semi-icons": "2.37.0-beta.0",
+        "@douyinfe/semi-illustrations": "2.37.0-beta.0",
+        "@douyinfe/semi-theme-default": "2.37.0-beta.0",
         "async-validator": "^3.5.0",
         "classnames": "^2.2.6",
         "copy-text-to-clipboard": "^2.1.1",

+ 15 - 1
packages/semi-ui/pagination/_story/pagination.stories.jsx

@@ -156,4 +156,18 @@ export const HideOnSingePageAndShowChanger = () => {
 
 HideOnSingePageAndShowChanger.story = {
   name: 'hideOnSingelePage & showSizeChanger at same time',
-};
+};
+
+export const DisabledPagination = () => {
+  return (
+    <div>
+      <Pagination total={100} disabled style={{ marginBottom: 12 }} size='small'></Pagination>
+      <Pagination total={100} disabled style={{ marginBottom: 12 }} showQuickJumper size='small'></Pagination>
+      <Pagination total={100} disabled style={{ marginBottom: 12 }} size='small' hoverShowPageSelect></Pagination>
+      <Pagination total={100} disabled style={{ marginBottom: 12 }} showQuickJumper></Pagination>
+      <Pagination total={100} disabled style={{ marginBottom: 12 }} showSizeChanger></Pagination>
+      <Pagination total={100} disabled style={{ marginBottom: 12 }} showTotal></Pagination>
+      <Pagination total={100} disabled style={{ marginBottom: 12 }} showTotal showSizeChanger showQuickJumper></Pagination>
+    </div>
+  )
+}

+ 37 - 23
packages/semi-ui/pagination/index.tsx

@@ -49,7 +49,8 @@ export interface PaginationProps {
     style?: React.CSSProperties;
     className?: string;
     hideOnSinglePage?: boolean;
-    hoverShowPageSelect?: boolean
+    hoverShowPageSelect?: boolean;
+    disabled?: boolean
 }
 
 export interface PaginationState {
@@ -93,6 +94,7 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
         hideOnSinglePage: PropTypes.bool,
         hoverShowPageSelect: PropTypes.bool,
         showQuickJumper: PropTypes.bool,
+        disabled: PropTypes.bool,
     };
 
     static defaultProps = {
@@ -110,6 +112,7 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
         className: '',
         hideOnSinglePage: false,
         showQuickJumper: false,
+        disabled: false,
     };
 
     constructor(props: PaginationProps) {
@@ -208,19 +211,20 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
     }
 
     renderPrevBtn() {
-        const { prevText } = this.props;
+        const { prevText, disabled } = this.props;
         const { prevDisabled } = this.state;
+        const isDisabled = prevDisabled || disabled;
         const preClassName = classNames({
             [`${prefixCls}-item`]: true,
             [`${prefixCls}-prev`]: true,
-            [`${prefixCls}-item-disabled`]: prevDisabled,
+            [`${prefixCls}-item-disabled`]: isDisabled,
         });
         return (
             <li
                 role="button"
-                aria-disabled={prevDisabled ? true : false}
+                aria-disabled={isDisabled ? true : false}
                 aria-label="Previous"
-                onClick={e => !prevDisabled && this.foundation.goPrev(e)}
+                onClick={e => !isDisabled && this.foundation.goPrev(e)}
                 className={preClassName}
                 x-semi-prop="prevText"
             >
@@ -230,19 +234,20 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
     }
 
     renderNextBtn() {
-        const { nextText } = this.props;
+        const { nextText, disabled } = this.props;
         const { nextDisabled } = this.state;
+        const isDisabled = nextDisabled || disabled;
         const nextClassName = classNames({
             [`${prefixCls}-item`]: true,
-            [`${prefixCls}-item-disabled`]: nextDisabled,
+            [`${prefixCls}-item-disabled`]: isDisabled,
             [`${prefixCls}-next`]: true,
         });
         return (
             <li
                 role="button"
-                aria-disabled={nextDisabled ? true : false}
+                aria-disabled={isDisabled ? true : false}
                 aria-label="Next"
-                onClick={e => !nextDisabled && this.foundation.goNext(e)}
+                onClick={e => !isDisabled && this.foundation.goNext(e)}
                 className={nextClassName}
                 x-semi-prop="prevText"
             >
@@ -255,7 +260,7 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
         // rtl modify the default position
         const { direction } = this.context;
         const defaultPopoverPosition = direction === 'rtl' ? 'bottomRight' : 'bottomLeft';
-        const { showSizeChanger, popoverPosition = defaultPopoverPosition } = this.props;
+        const { showSizeChanger, popoverPosition = defaultPopoverPosition, disabled } = this.props;
         const { pageSize } = this.state;
         const switchCls = classNames(`${prefixCls}-switch`);
         if (!showSizeChanger) {
@@ -277,6 +282,7 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
             <div className={switchCls}>
                 <Select
                     aria-label="Page size selector"
+                    disabled={disabled}
                     onChange={newPageSize => this.foundation.changePageSize(newPageSize)}
                     value={pageSize}
                     key={pageSize}
@@ -291,13 +297,13 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
     }
 
     renderQuickJump(locale: PaginationLocale) {
-        const { showQuickJumper } = this.props;
+        const { showQuickJumper, disabled } = this.props;
         const { quickJumpPage, total, pageSize } = this.state;
         if (!showQuickJumper) {
             return null;
         }
         const totalPageNum = this.foundation._getTotalPageNumber(total, pageSize);
-        const isDisabled = totalPageNum === 1;
+        const isDisabled = (totalPageNum === 1) || disabled;
         const quickJumpCls = classNames({
             [`${prefixCls}-quickjump`]: true,
             [`${prefixCls}-quickjump-disabled`]: isDisabled
@@ -327,17 +333,19 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
             restLeftPageList,
             restRightPageList,
         } = this.state;
-        const { popoverPosition, popoverZIndex } = this.props;
+        const { popoverPosition, popoverZIndex, disabled } = this.props;
 
         return pageList.map((page, i) => {
             const pageListClassName = classNames(`${prefixCls}-item`, {
                 [`${prefixCls}-item-active`]: currentPage === page,
+                [`${prefixCls}-item-all-disabled`]: disabled,
+                [`${prefixCls}-item-all-disabled-active`]: currentPage === page && disabled,
                 // [`${prefixCls}-item-rest-opening`]: (i < 3 && isLeftRestHover && page ==='...') || (i > 3 && isRightRestHover && page === '...')
             });
             const pageEl = (
                 <li
                     key={`${page}${i}`}
-                    onClick={() => this.foundation.goPage(page, i)}
+                    onClick={() => !disabled && this.foundation.goPage(page, i)}
                     className={pageListClassName}
                     aria-label={page === '...' ? 'More' : `Page ${page}`}
                     aria-current={currentPage === page ? "page" : false}
@@ -345,7 +353,7 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
                     {page}
                 </li>
             );
-            if (page === '...') {
+            if (page === '...' && !disabled) {
                 let content;
                 i < 3 ? (content = restLeftPageList) : (content = restRightPageList);
                 return (
@@ -405,8 +413,8 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
     }
 
     renderSmallPage(locale: PaginationLocale) {
-        const { className, style, hideOnSinglePage, hoverShowPageSelect, showSizeChanger } = this.props;
-        const paginationCls = classNames(`${prefixCls}-small`, prefixCls, className);
+        const { className, style, hideOnSinglePage, hoverShowPageSelect, showSizeChanger, disabled, ...rest } = this.props;
+        const paginationCls = classNames(`${prefixCls}-small`, prefixCls, className, { [`${prefixCls}-disabled`]: disabled });
         const { currentPage, total, pageSize } = this.state;
         const totalPageNum = Math.ceil(total / pageSize);
         if (totalPageNum < 2 && hideOnSinglePage && !showSizeChanger) {
@@ -416,13 +424,19 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
         const pageNumbers = Array.from({ length: Math.ceil(total / pageSize) }, (v, i) => i + 1);
         const pageList = this.renderRestPageList(pageNumbers);
 
-        const page = (<div className={`${prefixCls}-item ${prefixCls}-item-small`}>{currentPage}/{totalPageNum} </div>);
+        const pageCls = classNames({
+            [`${prefixCls}-item`]: true,
+            [`${prefixCls}-item-small`]: true,
+            [`${prefixCls}-item-all-disabled`]: disabled,
+        });
+
+        const page = (<div className={pageCls}>{currentPage}/{totalPageNum} </div>);
 
         return (
-            <div className={paginationCls} style={style}>
+            <div className={paginationCls} style={style} {...this.getDataAttr(rest)}>
                 {this.renderPrevBtn()}
                 {
-                    hoverShowPageSelect ? (
+                    (hoverShowPageSelect && !disabled) ? (
                         <Popover
                             content={pageList}
                         >
@@ -438,8 +452,8 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
 
     renderDefaultPage(locale: PaginationLocale) {
         const { total, pageSize } = this.state;
-        const { showTotal, className, style, hideOnSinglePage, showSizeChanger } = this.props;
-        const paginationCls = classNames(className, `${prefixCls}`);
+        const { showTotal, className, style, hideOnSinglePage, showSizeChanger, disabled, ...rest } = this.props;
+        const paginationCls = classNames(className, `${prefixCls}`, { [`${prefixCls}-disabled`]: disabled });
         const showTotalCls = `${prefixCls}-total`;
         const totalPageNum = Math.ceil(total / pageSize);
         if (totalPageNum < 2 && hideOnSinglePage && !showSizeChanger) {
@@ -450,7 +464,7 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
         const totalToken = locale.total.replace('${total}', totalNum.toString());
 
         return (
-            <ul className={paginationCls} style={style}>
+            <ul className={paginationCls} style={style} {...this.getDataAttr(rest)}>
                 {showTotal ? (
                     <span className={showTotalCls}>
                         {totalToken}

+ 5 - 0
packages/semi-ui/progress/index.tsx

@@ -2,6 +2,7 @@ import React, { ReactNode, Component } from 'react';
 import cls from 'classnames';
 import PropTypes from 'prop-types';
 import { cssClasses, strings } from '@douyinfe/semi-foundation/progress/constants';
+import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 import '@douyinfe/semi-foundation/progress/progress.scss';
 import { Animation } from '@douyinfe/semi-animation';
 import { Motion } from '../_base/base';
@@ -160,6 +161,7 @@ class Progress extends Component<ProgressProps, ProgressState> {
             percent,
             orbitStroke,
             id,
+            ...rest
         } = this.props;
         const ariaLabel = this.props['aria-label'];
         const ariaLabelledBy = this.props['aria-labelledby'];
@@ -205,6 +207,7 @@ class Progress extends Component<ProgressProps, ProgressState> {
                 aria-labelledby={ariaLabelledBy}
                 aria-label={ariaLabel}
                 aria-valuetext={ariaValueText}
+                {...getDataAttr(rest)}
             >
                 <svg key={size} className={classNames.svg} height={width} width={width} aria-hidden>
                     <circle
@@ -274,6 +277,7 @@ class Progress extends Component<ProgressProps, ProgressState> {
             percent,
             orbitStroke,
             id,
+            ...rest
         } = this.props;
         const ariaLabel = this.props['aria-label'];
         const ariaLabelledBy = this.props['aria-labelledby'];
@@ -318,6 +322,7 @@ class Progress extends Component<ProgressProps, ProgressState> {
                 aria-labelledby={ariaLabelledBy}
                 aria-label={ariaLabel}
                 aria-valuetext={ariaValueText}
+                {...getDataAttr(rest)}
             >
                 <div
                     className={progressTrackCls}

+ 3 - 1
packages/semi-ui/radio/radio.tsx

@@ -198,7 +198,8 @@ class Radio extends BaseComponent<RadioProps, RadioState> {
             mode,
             type,
             value: propValue,
-            name
+            name,
+            ...rest
         } = this.props;
 
         let realChecked,
@@ -295,6 +296,7 @@ class Radio extends BaseComponent<RadioProps, RadioState> {
                 className={wrapper}
                 onMouseEnter={this.handleMouseEnter}
                 onMouseLeave={this.handleMouseLeave}
+                {...this.getDataAttr(rest)}
             >
                 <RadioInner
                     {...this.props}

+ 2 - 0
packages/semi-ui/radio/radioGroup.tsx

@@ -148,6 +148,7 @@ class RadioGroup extends BaseComponent<RadioGroupProps, RadioGroupState> {
             type,
             buttonSize,
             id,
+            ...rest
         } = this.props;
 
         const isButtonRadio = type === strings.TYPE_BUTTON;
@@ -213,6 +214,7 @@ class RadioGroup extends BaseComponent<RadioGroupProps, RadioGroupState> {
                 aria-labelledby={this.props['aria-labelledby']}
                 aria-describedby={this.props['aria-describedby']}
                 aria-required={this.props['aria-required']}
+                {...this.getDataAttr(rest)}
             >
                 <Context.Provider
                     value={{

+ 2 - 1
packages/semi-ui/rating/index.tsx

@@ -309,7 +309,7 @@ export default class Rating extends BaseComponent<RatingProps, RatingState> {
     }
 
     render() {
-        const { style, prefixCls, disabled, className, id, count, tabIndex } = this.props;
+        const { style, prefixCls, disabled, className, id, count, tabIndex, ...rest } = this.props;
         const { value, emptyStarFocusVisible } = this.state;
         const ariaLabelPrefix = this.getAriaLabelPrefix();
         const ariaLabel = `Rating: ${value} of ${count} ${ariaLabelPrefix}${value === 1 ? '' : 's'},`;
@@ -337,6 +337,7 @@ export default class Rating extends BaseComponent<RatingProps, RatingState> {
                 onKeyDown={disabled ? noop : this.onKeyDown}
                 ref={this.saveRate as any}
                 id={id}
+                {...this.getDataAttr(rest)}
             >
                 {itemList}
             </ul>

+ 2 - 2
packages/semi-ui/scrollList/index.tsx

@@ -35,7 +35,7 @@ class ScrollList extends BaseComponent<ScrollListProps, {}> {
     }
 
     render() {
-        const { children, header, footer, prefixCls, bodyHeight, className, style } = this.props;
+        const { children, header, footer, prefixCls, bodyHeight, className, style, ...rest } = this.props;
 
         const clsWrapper = classnames(className, {
             [prefixCls || cssClasses.PREFIX]: true,
@@ -46,7 +46,7 @@ class ScrollList extends BaseComponent<ScrollListProps, {}> {
         });
 
         return (
-            <div className={clsWrapper} style={style}>
+            <div className={clsWrapper} style={style} {...this.getDataAttr(rest)}>
                 {header ? (
                     <div className={clsHeader}>
                         <div 

+ 3 - 1
packages/semi-ui/select/index.tsx

@@ -1308,7 +1308,8 @@ class Select extends BaseComponent<SelectProps, SelectState> {
             triggerRender,
             arrowIcon,
             clearIcon,
-            borderless
+            borderless,
+            ...rest
         } = this.props;
 
         const { selections, isOpen, keyboardEventSet, inputValue, isHovering, isFocus, showInput, focusIndex } = this.state;
@@ -1415,6 +1416,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
                 onBlur={e => this.foundation.handleTriggerBlur(e as any)}
                 onKeyPress={this.onKeyPress}
                 {...keyboardEventSet}
+                {...this.getDataAttr(rest)}
             >
                 {inner}
             </div>

+ 22 - 1
packages/semi-ui/sideSheet/SideSheetContent.tsx

@@ -6,6 +6,7 @@ import Button from '../iconButton';
 import { noop } from 'lodash';
 import { IconClose } from '@douyinfe/semi-icons';
 import { SideSheetProps } from "@douyinfe/semi-foundation/sideSheet/sideSheetFoundation";
+import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 
 let uuid = 0;
 const prefixCls = cssClasses.PREFIX;
@@ -167,6 +168,23 @@ export default class SideSheetContent extends React.PureComponent<SideSheetConte
             mask,
             className,
             width,
+            onClose,
+            maskStyle,
+            maskClosable,
+            maskClassName,
+            title,
+            closable,
+            headerStyle,
+            height,
+            style,
+            size,
+            bodyStyle,
+            dialogClassName,
+            children,
+            footer,
+            maskExtraProps,
+            wrapperExtraProps,
+            ...rest
         } = this.props;
         const wrapperCls = cls(className, {
             [`${prefixCls}-fixed`]: !mask,
@@ -176,8 +194,11 @@ export default class SideSheetContent extends React.PureComponent<SideSheetConte
         if (!mask && width) {
             wrapperStyle.width = width;
         }
+
+        const dataAttr = getDataAttr(rest);
+
         return (
-            <div className={wrapperCls} style={wrapperStyle}>
+            <div className={wrapperCls} style={wrapperStyle} {...dataAttr}>
                 {this.getMaskElement()}
                 {this.getDialogElement()}
             </div>

+ 2 - 1
packages/semi-ui/slider/index.tsx

@@ -565,7 +565,7 @@ export default class Slider extends BaseComponent<SliderProps, SliderState> {
 
     render() {
         const { disabled, currentValue, min, max } = this.state;
-        const { vertical, verticalReverse, style, railStyle, range, className } = this.props;
+        const { vertical, verticalReverse, style, railStyle, range, className, ...rest } = this.props;
         const wrapperClass = cls(
             `${prefixCls}-wrapper`,
             {
@@ -591,6 +591,7 @@ export default class Slider extends BaseComponent<SliderProps, SliderState> {
                 aria-label={ariaLabel}
                 onMouseEnter={() => this.foundation.handleWrapperEnter()}
                 onMouseLeave={() => this.foundation.handleWrapperLeave()}
+                {...this.getDataAttr(rest)}
             >
                 {// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
                     <div

+ 2 - 2
packages/semi-ui/spin/index.tsx

@@ -106,7 +106,7 @@ class Spin extends BaseComponent<SpinProps, SpinState> {
 
     render() {
         this.foundation.updateLoadingIfNeedDelay();
-        const { children, style, wrapperClassName, childStyle, size } = this.props;
+        const { children, style, wrapperClassName, childStyle, size, ...rest } = this.props;
         const { loading } = this.state;
         const spinCls = cls(
             prefixCls,
@@ -119,7 +119,7 @@ class Spin extends BaseComponent<SpinProps, SpinState> {
         );
 
         return (
-            <div className={spinCls} style={style}>
+            <div className={spinCls} style={style} {...this.getDataAttr(rest)}>
                 {this.renderSpin()}
                 <div className={`${prefixCls}-children`} style={childStyle} x-semi-prop="children">
                     {children}

+ 3 - 1
packages/semi-ui/steps/basicSteps.tsx

@@ -2,6 +2,7 @@ import React, { cloneElement, Children, useMemo, isValidElement, ReactElement }
 import PropTypes from 'prop-types';
 import cls from 'classnames';
 import { stepsClasses as css } from '@douyinfe/semi-foundation/steps/constants';
+import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 
 export type Direction = 'horizontal' | 'vertical';
 export type Status = 'wait' | 'process' | 'finish' | 'error' | 'warning';
@@ -34,6 +35,7 @@ const Steps = (props: BasicStepsProps) => {
         style,
         hasLine,
         onChange,
+        ...rest
     } = props;
     const inner = useMemo(() => {
         const filteredChildren = Children.toArray(children).filter(c => isValidElement(c)) as Array<ReactElement>;
@@ -81,7 +83,7 @@ const Steps = (props: BasicStepsProps) => {
     });
 
     return (
-        <div aria-label={props["aria-label"]} className={wrapperCls} style={style}>
+        <div aria-label={props["aria-label"]} className={wrapperCls} style={style} {...getDataAttr(rest)}>
             {inner}
         </div>
     );

+ 3 - 1
packages/semi-ui/steps/fillSteps.tsx

@@ -2,6 +2,7 @@ import React, { cloneElement, Children, useMemo, ReactElement, isValidElement }
 import PropTypes from 'prop-types';
 import cls from 'classnames';
 import { stepsClasses as css } from '@douyinfe/semi-foundation/steps/constants';
+import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 import { Row, Col } from '../grid';
 
 export type Status = 'wait' | 'process' | 'finish' | 'error' | 'warning';
@@ -21,7 +22,7 @@ export interface FillStepsProps {
 }
 
 const Steps = (props: FillStepsProps) => {
-    const { current, status, children, prefixCls, initial, direction, className, style, onChange } = props;
+    const { current, status, children, prefixCls, initial, direction, className, style, onChange, ...rest } = props;
     const inner = useMemo(() => {
         const filteredChildren = Children.toArray(children).filter(c => isValidElement(c)) as Array<ReactElement>;
         const colStyle = direction === 'vertical' ? null : { width: `${100 / filteredChildren.length}%` };
@@ -69,6 +70,7 @@ const Steps = (props: FillStepsProps) => {
             className={wrapperCls}
             style={style}
             aria-label={props["aria-label"]}
+            {...getDataAttr(rest)}
         >
             <Row type="flex" justify="start">
                 {inner}

+ 3 - 2
packages/semi-ui/steps/navSteps.tsx

@@ -1,5 +1,6 @@
 import React, { cloneElement, Children, useMemo, isValidElement, ReactElement } from 'react';
 import PropTypes from 'prop-types';
+import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 import cls from 'classnames';
 import { stepsClasses as css } from '@douyinfe/semi-foundation/steps/constants';
 
@@ -17,7 +18,7 @@ export interface NavStepsProps {
 }
 
 const Steps = (props: NavStepsProps) => {
-    const { size, current, initial, children, prefixCls, className, style, onChange } = props;
+    const { size, current, initial, children, prefixCls, className, style, onChange, ...rest } = props;
     const inner = useMemo(() => {
         const filteredChildren = Children.toArray(children).filter(c => isValidElement(c)) as Array<ReactElement>;
         const total = filteredChildren.length;
@@ -47,7 +48,7 @@ const Steps = (props: NavStepsProps) => {
     });
 
     return (
-        <div aria-label={props["aria-label"]} className={wrapperCls} style={style}>
+        <div aria-label={props["aria-label"]} className={wrapperCls} style={style} {...getDataAttr(rest)}>
             {inner}
         </div>
     );

+ 2 - 2
packages/semi-ui/switch/index.tsx

@@ -126,7 +126,7 @@ class Switch extends BaseComponent<SwitchProps, SwitchState> {
 
     render() {
         const { nativeControlChecked, nativeControlDisabled, focusVisible } = this.state;
-        const { className, style, onMouseEnter, onMouseLeave, size, checkedText, uncheckedText, loading, id } = this.props;
+        const { className, style, onMouseEnter, onMouseLeave, size, checkedText, uncheckedText, loading, id, ...rest } = this.props;
         const wrapperCls = cls(className, {
             [cssClasses.PREFIX]: true,
             [cssClasses.CHECKED]: nativeControlChecked,
@@ -145,7 +145,7 @@ class Switch extends BaseComponent<SwitchProps, SwitchState> {
         const showCheckedText = checkedText && nativeControlChecked && size !== 'small';
         const showUncheckedText = uncheckedText && !nativeControlChecked && size !== 'small';
         return (
-            <div className={wrapperCls} style={style} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
+            <div className={wrapperCls} style={style} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} {...this.getDataAttr(rest)}>
                 {loading ? (
                     <Spin wrapperClassName={cssClasses.LOADING_SPIN} size={size === 'default' ? 'middle' : size} />
                 ) : (

+ 3 - 0
packages/semi-ui/table/Table.tsx

@@ -1461,6 +1461,8 @@ class Table<RecordType extends Record<string, any>> extends BaseComponent<Normal
             setBodyHasScrollbar: this.setBodyHasScrollbar,
         };
 
+        const dataAttr = this.getDataAttr(rest);
+
         return (
             <div
                 ref={this.rootWrapRef}
@@ -1468,6 +1470,7 @@ class Table<RecordType extends Record<string, any>> extends BaseComponent<Normal
                 data-column-fixed={anyColumnFixed}
                 style={wrapStyle}
                 id={id}
+                {...dataAttr}
             >
                 <TableContextProvider {...tableContextValue} direction={props.direction}>
                     <Spin spinning={loading} size="large">

+ 1 - 2
packages/semi-ui/tabs/index.tsx

@@ -3,7 +3,6 @@ import cls from 'classnames';
 import PropTypes from 'prop-types';
 import { cssClasses, strings } from '@douyinfe/semi-foundation/tabs/constants';
 import isNullOrUndefined from '@douyinfe/semi-foundation/utils/isNullOrUndefined';
-import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 import TabsFoundation, { TabsAdapter } from '@douyinfe/semi-foundation/tabs/foundation';
 import { isEqual, pick } from 'lodash';
 import BaseComponent from '../_base/baseComponent';
@@ -293,7 +292,7 @@ class Tabs extends BaseComponent<TabsProps, TabsState> {
         const content = keepDOM ? children : this.getActiveItem();
 
         return (
-            <div className={tabWrapperCls} style={style} {...getDataAttr(restProps)}>
+            <div className={tabWrapperCls} style={style} {...this.getDataAttr(restProps)}>
                 {tabBar}
                 <TabsContext.Provider
                     value={{

+ 2 - 0
packages/semi-ui/tagInput/index.tsx

@@ -560,6 +560,7 @@ class TagInput extends BaseComponent<TagInputProps, TagInputState> {
             disabled,
             placeholder,
             validateStatus,
+            ...rest
         } = this.props;
 
         const {
@@ -602,6 +603,7 @@ class TagInput extends BaseComponent<TagInputProps, TagInputState> {
                 onClick={e => {
                     this.handleClick(e);
                 }}
+                {...this.getDataAttr(rest)}
             >
                 {this.renderPrefix()}
                 <div className={wrapperCls}>

+ 3 - 2
packages/semi-ui/timeline/index.tsx

@@ -3,6 +3,7 @@ import cls from 'classnames';
 import PropTypes from 'prop-types';
 import '@douyinfe/semi-foundation/timeline/timeline.scss';
 import { cssClasses, strings } from '@douyinfe/semi-foundation/timeline/constants';
+import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 import ConfigContext from '../configProvider/context';
 import Item, { TimelineItemProps } from './item';
 
@@ -72,7 +73,7 @@ class Timeline extends PureComponent<TimelineProps> {
     });
 
     render() {
-        const { children, className, style, mode, dataSource } = this.props;
+        const { children, className, style, mode, dataSource, ...rest } = this.props;
         const classString = cls(
             prefixCls,
             className,
@@ -88,7 +89,7 @@ class Timeline extends PureComponent<TimelineProps> {
         const items = childrenList || this.addClassName(children);
 
         return (
-            <ul aria-label={this.props['aria-label']} style={style} className={classString}>
+            <ul aria-label={this.props['aria-label']} style={style} className={classString} {...getDataAttr(rest)}>
                 {items}
             </ul>
         );

+ 3 - 1
packages/semi-ui/timeline/item.tsx

@@ -3,6 +3,7 @@ import cls from 'classnames';
 import { noop } from 'lodash';
 import PropTypes from 'prop-types';
 import { cssClasses, strings } from '@douyinfe/semi-foundation/timeline/constants';
+import getDataAttr from '@douyinfe/semi-foundation/utils/getDataAttr';
 import '@douyinfe/semi-foundation/timeline/timeline.scss';
 
 export interface TimelineItemProps {
@@ -50,6 +51,7 @@ export default class Item extends PureComponent<TimelineItemProps> {
             time,
             extra,
             onClick,
+            ...rest
         } = this.props;
 
         const itemCls = cls(prefixCls,
@@ -63,7 +65,7 @@ export default class Item extends PureComponent<TimelineItemProps> {
         });
         const dotStyle = color ? { style: { backgroundColor: color } } : null;
         return (
-            <li className={itemCls} style={style} onClick={onClick}>
+            <li className={itemCls} style={style} onClick={onClick} {...getDataAttr(rest)}>
                 <div className={`${prefixCls}-tail`} aria-hidden />
                 <div
                     className={dotCls}

+ 2 - 2
packages/semi-ui/transfer/index.tsx

@@ -690,7 +690,7 @@ class Transfer extends BaseComponent<TransferProps, TransferState> {
     }
 
     render() {
-        const { className, style, disabled, renderSelectedPanel, renderSourcePanel } = this.props;
+        const { className, style, disabled, renderSelectedPanel, renderSourcePanel, ...rest } = this.props;
         const transferCls = cls(prefixCls, className, {
             [`${prefixCls}-disabled`]: disabled,
             [`${prefixCls}-custom-panel`]: renderSelectedPanel && renderSourcePanel,
@@ -699,7 +699,7 @@ class Transfer extends BaseComponent<TransferProps, TransferState> {
         return (
             <LocaleConsumer componentName="Transfer">
                 {(locale: Locale['Transfer']) => (
-                    <div className={transferCls} style={style}>
+                    <div className={transferCls} style={style} {...this.getDataAttr(rest)}>
                         {this.renderLeft(locale)}
                         {this.renderRight(locale)}
                     </div>

+ 2 - 1
packages/semi-ui/tree/index.tsx

@@ -742,6 +742,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
             labelEllipsis,
             virtualize,
             checkRelation,
+            ...rest
         } = this.props;
         const wrapperCls = cls(`${prefixcls}-wrapper`, className);
         const listCls = cls(`${prefixcls}-option-list`, {
@@ -793,7 +794,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
                     labelEllipsis: typeof labelEllipsis === 'undefined' ? virtualize : labelEllipsis,
                 }}
             >
-                <div aria-label={this.props['aria-label']} className={wrapperCls} style={style}>
+                <div aria-label={this.props['aria-label']} className={wrapperCls} style={style} {...this.getDataAttr(rest)}>
                     {filterTreeNode ? this.renderInput() : null}
                     <div className={listCls} {...ariaAttr}>
                         {noData ? this.renderEmpty() : (multiple ? 

+ 3 - 1
packages/semi-ui/treeSelect/index.tsx

@@ -971,7 +971,8 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
             leafOnly,
             searchPosition,
             triggerRender,
-            borderless
+            borderless,
+            ...rest
         } = this.props;
         const { inputValue, selectedKeys, checkedKeys, keyEntities, isFocus } = this.state;
         const filterable = Boolean(filterTreeNode);
@@ -1064,6 +1065,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 aria-describedby={this.props['aria-describedby']}
                 aria-required={this.props['aria-required']}
                 {...mouseEvent}
+                {...this.getDataAttr(rest)}
             >
                 {inner}
             </div>

+ 2 - 1
packages/semi-ui/upload/index.tsx

@@ -665,6 +665,7 @@ class Upload extends BaseComponent<UploadProps, UploadState> {
             validateMessage,
             validateStatus,
             directory,
+            ...rest
         } = this.props;
         const uploadCls = cls(
             prefixCls,
@@ -686,7 +687,7 @@ class Upload extends BaseComponent<UploadProps, UploadState> {
         const dirProps = directory ? { directory: 'directory', webkitdirectory: 'webkitdirectory' } : {};
 
         return (
-            <div className={uploadCls} style={style} x-prompt-pos={promptPosition}>
+            <div className={uploadCls} style={style} x-prompt-pos={promptPosition} {...this.getDataAttr(rest)}>
                 <input
                     key={this.state.inputKey}
                     capture={capture}

+ 1 - 1
packages/semi-webpack/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-webpack-plugin",
-    "version": "2.36.0",
+    "version": "2.37.0-beta.0",
     "description": "",
     "author": "伍浩威 <[email protected]>",
     "homepage": "",

+ 0 - 79
yarn.lock

@@ -1488,15 +1488,6 @@
     "@douyinfe/semi-animation-styled" "2.23.2"
     classnames "^2.2.6"
 
-"@douyinfe/[email protected]":
-  version "2.35.0"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-animation-react/-/semi-animation-react-2.35.0.tgz#ab1455cfd970e3667c81749f0b00c037ecd5b46e"
-  integrity sha512-u7FWiKZRyGr9pIEeN8qAt9BkYLM7aKDIQYI9OjD0D5j5j6TmIZkzI+xJsnSXnNthVCrF0KRatKyy65j3DQ+xRA==
-  dependencies:
-    "@douyinfe/semi-animation" "2.12.0"
-    "@douyinfe/semi-animation-styled" "2.23.2"
-    classnames "^2.2.6"
-
 "@douyinfe/[email protected]":
   version "2.23.2"
   resolved "https://registry.npmjs.org/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.23.2.tgz#f18bc074515441c297cc636ed98521e249d093c9"
@@ -1517,13 +1508,6 @@
   dependencies:
     bezier-easing "^2.1.0"
 
-"@douyinfe/[email protected]":
-  version "2.35.0"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-animation/-/semi-animation-2.35.0.tgz#fc7f3cfac2d162debec8fc7fa303ef1817916f80"
-  integrity sha512-fnq/OrSw0qSwBBPX3IywUqNpyVbRV4tN71gH+IhNzhyLmcmrsF0ssSaj5T7IowDz+PTgAfuBwy9aL6MaxaBEqg==
-  dependencies:
-    bezier-easing "^2.1.0"
-
 "@douyinfe/[email protected]":
   version "2.33.1"
   resolved "https://registry.npmjs.org/@douyinfe/semi-foundation/-/semi-foundation-2.33.1.tgz#1dfe6233e35a4ed768cb580b0c9a677d1c34ffba"
@@ -1538,20 +1522,6 @@
     memoize-one "^5.2.1"
     scroll-into-view-if-needed "^2.2.24"
 
-"@douyinfe/[email protected]":
-  version "2.35.0"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-foundation/-/semi-foundation-2.35.0.tgz#a718ac702538ff743729bbdbaa8d6775bf306667"
-  integrity sha512-Qj9jMguKKWsYWxbNFlCMYV8VCMntoMmOEDWc5JgBENJE8l1PcU5x5qKTwFeSHQVu3A+Cw7xhnKdMh6FBDi+S8Q==
-  dependencies:
-    "@douyinfe/semi-animation" "2.12.0"
-    async-validator "^3.5.0"
-    classnames "^2.2.6"
-    date-fns "^2.29.3"
-    date-fns-tz "^1.3.8"
-    lodash "^4.17.21"
-    memoize-one "^5.2.1"
-    scroll-into-view-if-needed "^2.2.24"
-
 "@douyinfe/[email protected]", "@douyinfe/semi-icons@latest":
   version "2.33.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.33.1.tgz#8e2871d9bc0ab7e12df74e3c71802d53d69b7425"
@@ -1559,23 +1529,11 @@
   dependencies:
     classnames "^2.2.6"
 
-"@douyinfe/[email protected]", "@douyinfe/semi-icons@^2.0.0":
-  version "2.35.0"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-icons/-/semi-icons-2.35.0.tgz#33131e2edfb8b0d176f9c4f5d624da930770834a"
-  integrity sha512-et993+faccNSd+Lf2uE4DzZX4s3oJbzCMgHEJjeZOFQgAYpkyKRdpSC9hnix1czLvFvVTtb6WqlstRT0lDZ2Vw==
-  dependencies:
-    classnames "^2.2.6"
-
 "@douyinfe/[email protected]":
   version "2.33.1"
   resolved "https://registry.npmjs.org/@douyinfe/semi-illustrations/-/semi-illustrations-2.33.1.tgz#530ab851f4dc32a52221c4067c778c800b9b55d7"
   integrity sha512-tTTUN8QwnQiF++sk4VBNzfkG87aYZ4iUeqk2ys8/ymVUmCZQ7y46ys020GO1MfPHRR47OMFPI82FVcH1WQtE3g==
 
-"@douyinfe/[email protected]":
-  version "2.35.0"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-illustrations/-/semi-illustrations-2.35.0.tgz#dac63ac1d9bcc3d222a5799285be911bb30ab786"
-  integrity sha512-jtCyb6vX5ZcR0gdCTBaMdGm48n8b8hlWiJ1aqqGqZN/64uaFOShRFJZkL4i8U69siFHLQpFTVL1ZESHfL4TS4Q==
-
 "@douyinfe/[email protected]":
   version "2.23.2"
   resolved "https://registry.npmjs.org/@douyinfe/semi-scss-compile/-/semi-scss-compile-2.23.2.tgz#30884bb194ee9ae1e81877985e5663c3297c1ced"
@@ -1649,38 +1607,6 @@
   dependencies:
     glob "^7.1.6"
 
-"@douyinfe/[email protected]":
-  version "2.35.0"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-theme-default/-/semi-theme-default-2.35.0.tgz#517bd687982d07d1a884a7b94d1edc9e7c419d45"
-  integrity sha512-zK+khIps9FiQ2WU7IPMJbVPlqvTkh/qZk9I4leSM+J1YCLz3pxrRNsU/kCxwB53QI2hOOIb0VlmKLzmwPhD9HQ==
-  dependencies:
-    glob "^7.1.6"
-
-"@douyinfe/semi-ui@^2.0.0":
-  version "2.35.0"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-ui/-/semi-ui-2.35.0.tgz#f02a8ff046fc44fdccdbe8366a95ea5a92c5655a"
-  integrity sha512-mTJOlT4rm8OtDd0LMZjhAvDQCKAip32c0u5YXqz+stq55KXvE+vihisUO5aUK8HXodicyujsXO5YfTuLfaduWQ==
-  dependencies:
-    "@douyinfe/semi-animation" "2.35.0"
-    "@douyinfe/semi-animation-react" "2.35.0"
-    "@douyinfe/semi-foundation" "2.35.0"
-    "@douyinfe/semi-icons" "2.35.0"
-    "@douyinfe/semi-illustrations" "2.35.0"
-    "@douyinfe/semi-theme-default" "2.35.0"
-    async-validator "^3.5.0"
-    classnames "^2.2.6"
-    copy-text-to-clipboard "^2.1.1"
-    date-fns "^2.29.3"
-    date-fns-tz "^1.3.8"
-    lodash "^4.17.21"
-    prop-types "^15.7.2"
-    react-resizable "^1.8.0"
-    react-sortable-hoc "^2.0.0"
-    react-window "^1.8.2"
-    resize-observer-polyfill "^1.5.1"
-    scroll-into-view-if-needed "^2.2.24"
-    utility-types "^3.10.0"
-
 "@douyinfe/semi-ui@latest":
   version "2.33.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.33.1.tgz#3234ca96eb3560b8299bc9750fbe59446522d9bb"
@@ -11560,11 +11486,6 @@ eslint-plugin-react@^7.20.6, eslint-plugin-react@^7.24.0:
     semver "^6.3.0"
     string.prototype.matchall "^4.0.8"
 
-eslint-plugin-semi-design@^2.33.0:
-  version "2.35.0"
-  resolved "https://registry.npmjs.org/eslint-plugin-semi-design/-/eslint-plugin-semi-design-2.35.0.tgz#8e5037d65dc1e4a5a00cf5f6db9b40ec210e0647"
-  integrity sha512-mEQKp/KjHzZ1teA/TA1817x1GVgeVuDSnqVWQzy7NOl2jNDbAymkKyTHouxHgSEmilIT4Zx8kLMP/9APEB6EoA==
-
 eslint-rule-composer@^0.3.0:
   version "0.3.0"
   resolved "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"