1
0
Эх сурвалжийг харах

feat: pass data-* attribute to dom (#1630)

* feat: pass data-* attribute to dom, #1597
pointhalo 2 жил өмнө
parent
commit
d4b9f3276b
48 өөрчлөгдсөн 154 нэмэгдсэн , 75 устгасан
  1. 1 1
      packages/semi-foundation/utils/getDataAttr.ts
  2. 5 0
      packages/semi-ui/_base/baseComponent.tsx
  3. 1 0
      packages/semi-ui/anchor/index.tsx
  4. 3 1
      packages/semi-ui/autoComplete/index.tsx
  5. 2 2
      packages/semi-ui/banner/index.tsx
  6. 1 1
      packages/semi-ui/breadcrumb/index.tsx
  7. 1 1
      packages/semi-ui/breadcrumb/item.tsx
  8. 1 1
      packages/semi-ui/calendar/dayCalendar.tsx
  9. 1 1
      packages/semi-ui/calendar/monthCalendar.tsx
  10. 1 1
      packages/semi-ui/calendar/rangeCalendar.tsx
  11. 2 2
      packages/semi-ui/calendar/weekCalendar.tsx
  12. 6 5
      packages/semi-ui/carousel/index.tsx
  13. 1 0
      packages/semi-ui/cascader/index.tsx
  14. 1 0
      packages/semi-ui/checkbox/checkbox.tsx
  15. 1 0
      packages/semi-ui/checkbox/checkboxGroup.tsx
  16. 1 1
      packages/semi-ui/collapse/index.tsx
  17. 22 15
      packages/semi-ui/collapsible/index.tsx
  18. 2 1
      packages/semi-ui/datePicker/datePicker.tsx
  19. 3 2
      packages/semi-ui/descriptions/index.tsx
  20. 4 3
      packages/semi-ui/descriptions/item.tsx
  21. 2 2
      packages/semi-ui/empty/index.tsx
  22. 3 2
      packages/semi-ui/list/index.tsx
  23. 4 1
      packages/semi-ui/list/item.tsx
  24. 3 1
      packages/semi-ui/modal/ModalContent.tsx
  25. 3 2
      packages/semi-ui/navigation/index.tsx
  26. 4 4
      packages/semi-ui/pagination/index.tsx
  27. 5 0
      packages/semi-ui/progress/index.tsx
  28. 3 1
      packages/semi-ui/radio/radio.tsx
  29. 2 0
      packages/semi-ui/radio/radioGroup.tsx
  30. 2 1
      packages/semi-ui/rating/index.tsx
  31. 2 2
      packages/semi-ui/scrollList/index.tsx
  32. 3 1
      packages/semi-ui/select/index.tsx
  33. 22 1
      packages/semi-ui/sideSheet/SideSheetContent.tsx
  34. 2 1
      packages/semi-ui/slider/index.tsx
  35. 2 2
      packages/semi-ui/spin/index.tsx
  36. 3 1
      packages/semi-ui/steps/basicSteps.tsx
  37. 3 1
      packages/semi-ui/steps/fillSteps.tsx
  38. 3 2
      packages/semi-ui/steps/navSteps.tsx
  39. 2 2
      packages/semi-ui/switch/index.tsx
  40. 3 0
      packages/semi-ui/table/Table.tsx
  41. 1 2
      packages/semi-ui/tabs/index.tsx
  42. 2 0
      packages/semi-ui/tagInput/index.tsx
  43. 3 2
      packages/semi-ui/timeline/index.tsx
  44. 3 1
      packages/semi-ui/timeline/item.tsx
  45. 2 2
      packages/semi-ui/transfer/index.tsx
  46. 2 1
      packages/semi-ui/tree/index.tsx
  47. 3 1
      packages/semi-ui/treeSelect/index.tsx
  48. 2 1
      packages/semi-ui/upload/index.tsx

+ 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;

+ 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/datePicker.tsx

@@ -862,7 +862,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 }),
@@ -872,6 +872,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>

+ 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}

+ 4 - 4
packages/semi-ui/pagination/index.tsx

@@ -405,7 +405,7 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
     }
 
     renderSmallPage(locale: PaginationLocale) {
-        const { className, style, hideOnSinglePage, hoverShowPageSelect, showSizeChanger } = this.props;
+        const { className, style, hideOnSinglePage, hoverShowPageSelect, showSizeChanger, ...rest } = this.props;
         const paginationCls = classNames(`${prefixCls}-small`, prefixCls, className);
         const { currentPage, total, pageSize } = this.state;
         const totalPageNum = Math.ceil(total / pageSize);
@@ -419,7 +419,7 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
         const page = (<div className={`${prefixCls}-item ${prefixCls}-item-small`}>{currentPage}/{totalPageNum} </div>);
 
         return (
-            <div className={paginationCls} style={style}>
+            <div className={paginationCls} style={style} {...this.getDataAttr(rest)}>
                 {this.renderPrevBtn()}
                 {
                     hoverShowPageSelect ? (
@@ -438,7 +438,7 @@ 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 { showTotal, className, style, hideOnSinglePage, showSizeChanger, ...rest } = this.props;
         const paginationCls = classNames(className, `${prefixCls}`);
         const showTotalCls = `${prefixCls}-total`;
         const totalPageNum = Math.ceil(total / pageSize);
@@ -450,7 +450,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}