diff --git a/.changeset/react-19-types-compat.md b/.changeset/react-19-types-compat.md new file mode 100644 index 000000000..f23ebeca3 --- /dev/null +++ b/.changeset/react-19-types-compat.md @@ -0,0 +1,5 @@ +--- +'@tiny-design/react': patch +--- + +Fix TypeScript compatibility with React 19. Component prop interfaces used the pattern `React.PropsWithRef`, which relied on a globally-augmented `JSX` namespace that `@types/react@19` no longer provides. The resolved prop types collapsed to `any`, silently dropping every intrinsic HTML attribute (`onClick`, `type`, `disabled`, `aria-*`, `children`, etc.) for consumers on React 19. Replaced the pattern with `React.ComponentProps<'x'>` / `React.ComponentPropsWithoutRef<'x'>` across ~60 component type files. Works on React 18 and 19. diff --git a/packages/react/REFS.md b/packages/react/REFS.md new file mode 100644 index 000000000..5fba0d929 --- /dev/null +++ b/packages/react/REFS.md @@ -0,0 +1,25 @@ +# Ref Conventions + +This package treats `ref` as an explicit component capability, not an accidental side effect of inheriting DOM props. + +## Rules + +- Use `React.ComponentPropsWithoutRef<'tag'>` for DOM-derived prop interfaces by default. +- Only expose `ref` when the component intentionally supports DOM access through `React.forwardRef`. +- Do not rely on `React.ComponentProps<'tag'>` to leak `ref` into props interfaces. +- If a component is primarily compositional or layout-only, do not expose `ref` unless there is a concrete DOM integration need. + +## Components That Should Expose `ref` + +- Interactive controls and focus targets such as `Button`, `Input`, `NativeSelect`, `Link`, `Switch`, `Checkbox`, `Radio`. +- Components frequently used for measurement, scrolling, positioning, or animation integration such as `Tree`, `Slider`, `Grid`, `Space`, `Flex`, `Layout`, `Anchor`, `Waterfall`, `Transfer`, `Steps`, and typography primitives. + +## Components That Usually Should Not Expose `ref` + +- Grouping or compositional wrappers such as `Button.Group`, `Checkbox.Group`, `Radio.Group`, `Input.Group`, `Input.Addon`, `Form.Item`, `Descriptions`, `Descriptions.Item`. + +## Practical Guidance + +- If consumers need the outer wrapper and an inner native control, expose both intentionally, for example `Checkbox` plus `checkboxRef`, or `Radio` plus `radioRef`. +- If a component renders different DOM elements by state, make the forwarded ref type reflect that reality instead of pretending it always points to one tag. +- When adding a new forwarded ref, add a focused test that proves the ref resolves to the expected DOM node. diff --git a/packages/react/src/alert/types.ts b/packages/react/src/alert/types.ts index fe3445f68..11a21d034 100644 --- a/packages/react/src/alert/types.ts +++ b/packages/react/src/alert/types.ts @@ -5,7 +5,7 @@ export type AlertType = 'success' | 'info' | 'warning' | 'error'; export interface AlertProps extends BaseProps, - Omit, 'title'> { + Omit, 'title'> { /** alert title */ title?: string | ReactNode; diff --git a/packages/react/src/anchor/__tests__/anchor.test.tsx b/packages/react/src/anchor/__tests__/anchor.test.tsx index 0320d22c7..9e51cbd7c 100644 --- a/packages/react/src/anchor/__tests__/anchor.test.tsx +++ b/packages/react/src/anchor/__tests__/anchor.test.tsx @@ -32,4 +32,16 @@ describe('', () => { expect(getByText('Link 1')).toBeInTheDocument(); expect(getByText('Link 2')).toBeInTheDocument(); }); + + it('should forward ref to root list element', () => { + const ref = React.createRef(); + + render( + + + + ); + + expect(ref.current).toBeInstanceOf(HTMLUListElement); + }); }); diff --git a/packages/react/src/anchor/anchor.tsx b/packages/react/src/anchor/anchor.tsx index 64ae2ebb0..a1d2623a1 100755 --- a/packages/react/src/anchor/anchor.tsx +++ b/packages/react/src/anchor/anchor.tsx @@ -7,7 +7,20 @@ import { AnchorLinkProps, AnchorProps } from './types'; import { AnchorContext } from './anchor-context'; import Sticky from '../sticky'; -const Anchor = (props: AnchorProps): JSX.Element => { +function assignRef(ref: React.Ref | undefined, value: T | null): void { + if (!ref) { + return; + } + + if (typeof ref === 'function') { + ref(value); + return; + } + + (ref as React.MutableRefObject).current = value; +} + +const Anchor = React.forwardRef((props, ref): JSX.Element => { const { affix = false, offsetTop = 0, @@ -20,6 +33,7 @@ const Anchor = (props: AnchorProps): JSX.Element => { style, children, prefixCls: customisedCls, + ...otherProps } = props; const configContext = useContext(ConfigContext); const prefixCls = getPrefixCls('anchor', configContext.prefixCls, customisedCls); @@ -37,6 +51,14 @@ const Anchor = (props: AnchorProps): JSX.Element => { linksRef.current.delete(href); }, []); + const setAnchorNode = useCallback( + (node: HTMLUListElement | null) => { + anchorRef.current = node; + assignRef(ref, node); + }, + [ref] + ); + const updateInk = useCallback(() => { const anchorEl = anchorRef.current; if (!anchorEl) return; @@ -200,7 +222,7 @@ const Anchor = (props: AnchorProps): JSX.Element => { ); const anchorContent = ( -
    +
      @@ -228,7 +250,7 @@ const Anchor = (props: AnchorProps): JSX.Element => { )} ); -}; +}); Anchor.displayName = 'Anchor'; diff --git a/packages/react/src/anchor/types.ts b/packages/react/src/anchor/types.ts index 41b6216dc..95138a443 100644 --- a/packages/react/src/anchor/types.ts +++ b/packages/react/src/anchor/types.ts @@ -1,7 +1,9 @@ import React from 'react'; import { BaseProps } from '../_utils/props'; -export interface AnchorProps extends BaseProps { +export interface AnchorProps + extends BaseProps, + Omit, 'children' | 'onChange' | 'onClick'> { affix?: boolean; type?: 'dot' | 'line'; offsetBottom?: number; @@ -12,7 +14,7 @@ export interface AnchorProps extends BaseProps { children?: React.ReactNode; } -export interface AnchorLinkProps extends BaseProps, React.PropsWithRef { +export interface AnchorLinkProps extends BaseProps, React.ComponentPropsWithoutRef<'a'> { href: string; title: string; children?: React.ReactElement[]; diff --git a/packages/react/src/aspect-ratio/types.ts b/packages/react/src/aspect-ratio/types.ts index 5f769bed2..c952daf36 100644 --- a/packages/react/src/aspect-ratio/types.ts +++ b/packages/react/src/aspect-ratio/types.ts @@ -3,7 +3,7 @@ import { BaseProps } from '../_utils/props'; export interface AspectRatioProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'div'> { /** the width of the content */ width?: number | string; diff --git a/packages/react/src/avatar/types.ts b/packages/react/src/avatar/types.ts index 29962204f..e1161d99d 100644 --- a/packages/react/src/avatar/types.ts +++ b/packages/react/src/avatar/types.ts @@ -6,7 +6,7 @@ export type AvatarPresence = 'online' | 'busy' | 'away' | 'offline'; export interface AvatarProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'span'> { /** use an icon */ icon?: React.ReactNode; @@ -28,7 +28,7 @@ export interface AvatarProps export interface AvatarGroupProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'span'> { /** the distance between two avatars */ gap: number | string; } diff --git a/packages/react/src/badge/types.ts b/packages/react/src/badge/types.ts index f18b8d95b..744be720b 100644 --- a/packages/react/src/badge/types.ts +++ b/packages/react/src/badge/types.ts @@ -3,7 +3,7 @@ import { BaseProps } from '../_utils/props'; export interface BadgeProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'span'> { /** the number to show in badge */ count?: React.ReactNode; diff --git a/packages/react/src/breadcrumb/types.ts b/packages/react/src/breadcrumb/types.ts index 5e657efb7..9776eed67 100644 --- a/packages/react/src/breadcrumb/types.ts +++ b/packages/react/src/breadcrumb/types.ts @@ -3,14 +3,14 @@ import { BaseProps } from '../_utils/props'; export interface BreadcrumbProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'nav'> { separator?: React.ReactNode; children: ReactElement | ReactElement[]; } export interface BreadcrumbItemProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'li'> { separator?: React.ReactNode; children?: React.ReactNode; } diff --git a/packages/react/src/button/button-group.tsx b/packages/react/src/button/button-group.tsx index 38e0266a1..36f9c666e 100755 --- a/packages/react/src/button/button-group.tsx +++ b/packages/react/src/button/button-group.tsx @@ -8,76 +8,74 @@ import { BUTTON_MARK } from './button'; const hasOwnProp = (props: T, key: keyof T): boolean => props[key] !== undefined; -const ButtonGroup = React.forwardRef( - (props: ButtonGroupProps, ref) => { - const { - size = 'md', - variant = 'solid', - color = 'default', - disabled = false, - round = false, - shape, - inheritMode = 'fill', - prefixCls: customisedCls, - className, - children, - ...otherProps - } = props; - const configContext = useContext(ConfigContext); - const prefixCls = getPrefixCls('btn-group', configContext.prefixCls, customisedCls); - const btnSize = props.size || configContext.componentSize || size; - const resolvedShape = shape || (round ? 'round' : 'default'); - const cls = classNames( - prefixCls, - { - [`${prefixCls}_round`]: resolvedShape === 'round', - [`${prefixCls}_variant-${variant}`]: variant, - [`${prefixCls}_color-${color}`]: color, - [`${prefixCls}_${btnSize}`]: btnSize, - }, - className - ); - return ( -
      - {React.Children.map(children, (child) => { - if (!React.isValidElement(child)) { - return child; - } +const ButtonGroup = (props: ButtonGroupProps): React.ReactElement => { + const { + size = 'md', + variant = 'solid', + color = 'default', + disabled = false, + round = false, + shape, + inheritMode = 'fill', + prefixCls: customisedCls, + className, + children, + ...otherProps + } = props; + const configContext = useContext(ConfigContext); + const prefixCls = getPrefixCls('btn-group', configContext.prefixCls, customisedCls); + const btnSize = props.size || configContext.componentSize || size; + const resolvedShape = shape || (round ? 'round' : 'default'); + const cls = classNames( + prefixCls, + { + [`${prefixCls}_round`]: resolvedShape === 'round', + [`${prefixCls}_variant-${variant}`]: variant, + [`${prefixCls}_color-${color}`]: color, + [`${prefixCls}_${btnSize}`]: btnSize, + }, + className + ); + return ( +
      + {React.Children.map(children, (child) => { + if (!React.isValidElement(child)) { + return child; + } - const childType = child.type as Record; - if (!childType[BUTTON_MARK]) { - return child; - } + const childType = child.type as unknown as Record; + if (!childType[BUTTON_MARK]) { + return child; + } - if (inheritMode === 'none') { - return child; - } + if (inheritMode === 'none') { + return child; + } - const childProps: Partial = {}; - const applyProp = (key: K, value: ButtonProps[K]): void => { - if (inheritMode === 'override' || !hasOwnProp(child.props, key)) { - childProps[key] = value; - } - }; + const childProps: Partial = {}; + const applyProp = (key: K, value: ButtonProps[K]): void => { + if (inheritMode === 'override' || !hasOwnProp(child.props, key)) { + childProps[key] = value; + } + }; - applyProp('variant', variant as ButtonVariant); - applyProp('color', color as ButtonColor); - applyProp('size', btnSize as SizeType); - applyProp('disabled', disabled); + applyProp('variant', variant as ButtonVariant); + applyProp('color', color as ButtonColor); + applyProp('size', btnSize as SizeType); + applyProp('disabled', disabled); - if ( - inheritMode === 'override' || - (!hasOwnProp(child.props, 'shape') && !hasOwnProp(child.props, 'round')) - ) { - childProps.shape = resolvedShape as ButtonShape; - } + if ( + inheritMode === 'override' || + (!hasOwnProp(child.props, 'shape') && !hasOwnProp(child.props, 'round')) + ) { + childProps.shape = resolvedShape as ButtonShape; + } - return React.cloneElement(child, childProps); - })} -
      - ); - } -); + return React.cloneElement(child, childProps); + })} +
      + ); +}; ButtonGroup.displayName = 'ButtonGroup'; diff --git a/packages/react/src/button/button.tsx b/packages/react/src/button/button.tsx index 7ea41cae9..ecdd57bad 100644 --- a/packages/react/src/button/button.tsx +++ b/packages/react/src/button/button.tsx @@ -5,6 +5,7 @@ import { getPrefixCls } from '../_utils/general'; import { ButtonProps } from './types'; export const BUTTON_MARK = Symbol('tiny-design.button'); +const isProduction = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env?.NODE_ENV === 'production'; const Button = React.forwardRef((props: ButtonProps, ref) => { const { @@ -37,7 +38,7 @@ const Button = React.forwardRef((props: ButtonPr const accessibleName = otherProps['aria-label'] || otherProps['aria-labelledby'] || otherProps.title; - if (process.env.NODE_ENV !== 'production' && isIconOnly && !accessibleName) { + if (!isProduction && isIconOnly && !accessibleName) { // Icon-only buttons need an accessible name. console.warn( 'Button with icon only should provide `aria-label`, `aria-labelledby`, or `title`.' diff --git a/packages/react/src/button/types.ts b/packages/react/src/button/types.ts index 3c8f562bd..ae2255f12 100644 --- a/packages/react/src/button/types.ts +++ b/packages/react/src/button/types.ts @@ -12,7 +12,7 @@ export type ButtonIconPosition = 'start' | 'end'; export type ButtonGroupInheritMode = 'fill' | 'override' | 'none'; export interface ButtonProps - extends BaseProps, React.PropsWithRef { + extends BaseProps, React.ComponentPropsWithoutRef<'button'> { variant?: ButtonVariant; color?: ButtonColor; loading?: boolean; @@ -27,7 +27,7 @@ export interface ButtonProps } export interface ButtonGroupProps - extends BaseProps, React.PropsWithRef { + extends BaseProps, React.ComponentPropsWithoutRef<'div'> { variant?: ButtonVariant; color?: ButtonColor; size?: SizeType; diff --git a/packages/react/src/calendar/types.ts b/packages/react/src/calendar/types.ts index 2573729e0..c35c0a130 100644 --- a/packages/react/src/calendar/types.ts +++ b/packages/react/src/calendar/types.ts @@ -7,7 +7,7 @@ export type SelectionMode = 'single' | 'range' | 'multiple'; export interface CalendarProps extends BaseProps, - Omit, 'onChange' | 'onSelect' | 'defaultValue'> { + Omit, 'onChange' | 'onSelect' | 'defaultValue'> { /** Selected date (controlled, single mode) */ defaultValue?: Date; /** Controlled selected date */ diff --git a/packages/react/src/card/types.ts b/packages/react/src/card/types.ts index 9dd335a9f..82e7ddaa0 100644 --- a/packages/react/src/card/types.ts +++ b/packages/react/src/card/types.ts @@ -3,14 +3,14 @@ import { BaseProps } from '../_utils/props'; export type CardVariant = 'outlined' | 'elevated' | 'filled'; -export interface CardContentProps extends React.PropsWithoutRef { +export interface CardContentProps extends React.ComponentPropsWithoutRef<'div'> { prefixCls?: string; children: ReactNode; } export interface CardProps extends BaseProps, - Omit, 'title'> { + Omit, 'title'> { title?: ReactNode; extra?: ReactNode; /** Card surface style */ diff --git a/packages/react/src/cascader/types.ts b/packages/react/src/cascader/types.ts index a5a6dd0ef..a25be7ed4 100644 --- a/packages/react/src/cascader/types.ts +++ b/packages/react/src/cascader/types.ts @@ -13,7 +13,7 @@ export type CascaderValue = (string | number)[]; export interface CascaderProps extends BaseProps, - Omit, 'onChange' | 'defaultValue'> { + Omit, 'onChange' | 'defaultValue'> { options: CascaderOption[]; value?: CascaderValue; defaultValue?: CascaderValue; diff --git a/packages/react/src/checkbox/checkbox-group.tsx b/packages/react/src/checkbox/checkbox-group.tsx index eafd00abb..df8b12a39 100755 --- a/packages/react/src/checkbox/checkbox-group.tsx +++ b/packages/react/src/checkbox/checkbox-group.tsx @@ -5,53 +5,51 @@ import { getPrefixCls } from '../_utils/general'; import { CheckboxGroupContext } from './checkbox-group-context'; import { CheckboxGroupProps } from './types'; -const CheckboxGroup = React.forwardRef( - (props: CheckboxGroupProps, ref): React.ReactElement => { - const { - defaultValue = [], - prefixCls: customisedCls, - onChange, - disabled, - className, - children, - ...otherProps - } = props; - const configContext = useContext(ConfigContext); - const prefixCls = getPrefixCls('checkbox-group', configContext.prefixCls, customisedCls); - const cls = classNames(prefixCls, className); - const [value, setValue] = useState( - 'value' in props ? (props.value as string[]) : defaultValue - ); +const CheckboxGroup = (props: CheckboxGroupProps): React.ReactElement => { + const { + defaultValue = [], + prefixCls: customisedCls, + onChange, + disabled, + className, + children, + ...otherProps + } = props; + const configContext = useContext(ConfigContext); + const prefixCls = getPrefixCls('checkbox-group', configContext.prefixCls, customisedCls); + const cls = classNames(prefixCls, className); + const [value, setValue] = useState( + 'value' in props ? (props.value as string[]) : defaultValue + ); - const itemOnChange = (e: React.ChangeEvent): void => { - if (!disabled) { - const name = e.currentTarget.name; - const newValue = value.includes(name) - ? value.filter((v) => v !== name) - : [...value, name]; - !('value' in props) && setValue(newValue); - onChange && onChange(newValue); - } - }; + const itemOnChange = (e: React.ChangeEvent): void => { + if (!disabled) { + const name = e.currentTarget.name; + const newValue = value.includes(name) + ? value.filter((v) => v !== name) + : [...value, name]; + !('value' in props) && setValue(newValue); + onChange && onChange(newValue); + } + }; - useEffect(() => { - 'value' in props && setValue([...(props.value as string[])]); - }, [props]); + useEffect(() => { + 'value' in props && setValue([...(props.value as string[])]); + }, [props]); - return ( - -
      - {children} -
      -
      - ); - } -); + return ( + +
      + {children} +
      +
      + ); +}; CheckboxGroup.displayName = 'CheckboxGroup'; diff --git a/packages/react/src/checkbox/types.ts b/packages/react/src/checkbox/types.ts index 7df56e080..d6902bf70 100644 --- a/packages/react/src/checkbox/types.ts +++ b/packages/react/src/checkbox/types.ts @@ -3,7 +3,7 @@ import { BaseProps } from '../_utils/props'; export interface CheckboxGroupProps extends BaseProps, - Omit, 'onChange'> { + Omit, 'onChange'> { defaultValue?: string[]; value?: string[]; onChange?: (checkedValues: string[]) => void; @@ -13,7 +13,7 @@ export interface CheckboxGroupProps export interface CheckboxProps extends BaseProps, - Omit, 'onChange'> { + Omit, 'onChange'> { /** Only required when use checkbox group */ value?: string; defaultChecked?: boolean; diff --git a/packages/react/src/collapse/collapse-panel.tsx b/packages/react/src/collapse/collapse-panel.tsx index 829844433..35e1c35dc 100644 --- a/packages/react/src/collapse/collapse-panel.tsx +++ b/packages/react/src/collapse/collapse-panel.tsx @@ -2,12 +2,7 @@ import React, { useEffect, useId, useState } from 'react'; import classNames from 'classnames'; import { ArrowDown } from '../_utils/components'; import CollapseTransition from '../collapse-transition'; -import { - CollapseCollapsible, - CollapseExpandIconRender, - CollapseItem, - CollapseRenderState, -} from './types'; +import { CollapseCollapsible, CollapseExpandIconRender, CollapseItem, CollapseRenderState } from './types'; type CollapsePanelProps = { prefixCls: string; @@ -28,10 +23,10 @@ type CollapsePanelProps = { onToggle: (key: string, event: React.MouseEvent) => void; }; -const renderContent = React.ReactNode)>( - content: T, +const renderContent = ( + content: React.ReactNode | ((args: CollapseRenderState) => React.ReactNode), state: CollapseRenderState -) => { +): React.ReactNode => { return typeof content === 'function' ? content(state) : content; }; diff --git a/packages/react/src/color-picker/types.ts b/packages/react/src/color-picker/types.ts index 538c74ce6..c2f8351fe 100644 --- a/packages/react/src/color-picker/types.ts +++ b/packages/react/src/color-picker/types.ts @@ -17,7 +17,7 @@ export interface ColorChangeMeta { export interface ColorPickerProps extends BaseProps, - Omit, 'onChange' | 'defaultValue'> { + Omit, 'onChange' | 'defaultValue'> { value?: string; defaultValue?: string; onChange?: (color: string, meta: ColorChangeMeta) => void; diff --git a/packages/react/src/copy-to-clipboard/types.ts b/packages/react/src/copy-to-clipboard/types.ts index ed7a378a1..250ceabb4 100644 --- a/packages/react/src/copy-to-clipboard/types.ts +++ b/packages/react/src/copy-to-clipboard/types.ts @@ -3,7 +3,7 @@ import { BaseProps } from '../_utils/props'; export interface CopyToClipboardProps extends BaseProps, - Omit, 'onCopy'> { + Omit, 'onCopy'> { text: string; onCopy?: (copied: boolean, text: string) => void; children?: React.ReactNode; diff --git a/packages/react/src/descriptions/col.tsx b/packages/react/src/descriptions/col.tsx index a304ee927..1c9d36567 100755 --- a/packages/react/src/descriptions/col.tsx +++ b/packages/react/src/descriptions/col.tsx @@ -15,6 +15,7 @@ const Col = (props: Props): React.ReactElement => { const { item, colon, type, bordered, prefixCls } = props; const { label, children, span = 1 } = item.props; + const resolvedSpan = typeof span === 'number' ? span : 1; switch (type) { case 'item': { const labelCls = classNames(`${prefixCls}__item-label`, { @@ -26,14 +27,14 @@ const Col = (props: Props): React.ReactElement => { {label} - + {children} ); } else { return ( - + {label} {children} @@ -46,7 +47,7 @@ const Col = (props: Props): React.ReactElement => { `${prefixCls}__item-label` ); return ( - + {label} ); @@ -54,7 +55,7 @@ const Col = (props: Props): React.ReactElement => { case 'content': { const cls = classNames({ [`${prefixCls}__item`]: !bordered }, `${prefixCls}__item-content`); return ( - + {children} ); diff --git a/packages/react/src/descriptions/descriptions.tsx b/packages/react/src/descriptions/descriptions.tsx index 4dad235f9..4ccc90187 100644 --- a/packages/react/src/descriptions/descriptions.tsx +++ b/packages/react/src/descriptions/descriptions.tsx @@ -14,6 +14,8 @@ import { DescriptionsSpan, } from './types'; +const isProduction = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env?.NODE_ENV === 'production'; + type NormalizedItem = DescriptionsItemType & { key: React.Key; content: React.ReactNode; @@ -119,7 +121,7 @@ const Descriptions = (props: DescriptionsProps): React.ReactElement => { const normalizedItems = useMemo(() => { if (items && items.length > 0) { - if (process.env.NODE_ENV !== 'production' && React.Children.count(children) > 0) { + if (!isProduction && React.Children.count(children) > 0) { warning(true, 'Descriptions `items` takes priority over `children`.'); } diff --git a/packages/react/src/descriptions/types.ts b/packages/react/src/descriptions/types.ts index 0e4b603c4..3e4466edc 100644 --- a/packages/react/src/descriptions/types.ts +++ b/packages/react/src/descriptions/types.ts @@ -17,7 +17,7 @@ export interface DescriptionsItemType extends BaseProps { export interface DescriptionsProps extends BaseProps, - Omit, 'title'> { + Omit, 'title'> { title?: React.ReactNode; extra?: React.ReactNode; footer?: React.ReactNode; diff --git a/packages/react/src/divider/types.ts b/packages/react/src/divider/types.ts index da583f804..0e60189bb 100644 --- a/packages/react/src/divider/types.ts +++ b/packages/react/src/divider/types.ts @@ -7,7 +7,7 @@ export type DividerVariant = 'solid' | 'dashed' | 'dotted'; export interface DividerProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'div'> { orientation?: DividerOrientation; variant?: DividerVariant; titlePlacement?: DividerTitlePlacement; diff --git a/packages/react/src/empty/types.ts b/packages/react/src/empty/types.ts index 3f24952e8..770ec4f3d 100644 --- a/packages/react/src/empty/types.ts +++ b/packages/react/src/empty/types.ts @@ -1,7 +1,7 @@ import React, { CSSProperties, ReactNode } from 'react'; import { BaseProps } from '../_utils/props'; -export interface EmptyProps extends BaseProps, React.PropsWithoutRef { +export interface EmptyProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> { image?: string | ReactNode; imageStyle?: CSSProperties; description?: boolean | string | React.ReactNode; diff --git a/packages/react/src/flex/types.ts b/packages/react/src/flex/types.ts index 592673521..dca1bfc37 100644 --- a/packages/react/src/flex/types.ts +++ b/packages/react/src/flex/types.ts @@ -1,7 +1,7 @@ import React from 'react'; import { BaseProps, SizeType } from '../_utils/props'; -export interface FlexProps extends BaseProps, React.PropsWithRef { +export interface FlexProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> { vertical?: boolean; wrap?: React.CSSProperties['flexWrap']; justify?: React.CSSProperties['justifyContent']; diff --git a/packages/react/src/flip/types.ts b/packages/react/src/flip/types.ts index b3688c057..0599a168c 100644 --- a/packages/react/src/flip/types.ts +++ b/packages/react/src/flip/types.ts @@ -1,7 +1,7 @@ import React from 'react'; import { BaseProps, DirectionType } from '../_utils/props'; -export interface FlipProps extends BaseProps, React.PropsWithoutRef { +export interface FlipProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> { /** a certain parent width and height to prevent the hover empty issue */ width: string | number; height: string | number; @@ -16,6 +16,6 @@ export interface FlipProps extends BaseProps, React.PropsWithoutRef, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'div'> { children?: React.ReactNode; } diff --git a/packages/react/src/form/__tests__/form.test.tsx b/packages/react/src/form/__tests__/form.test.tsx index 48465459f..a3cc30390 100644 --- a/packages/react/src/form/__tests__/form.test.tsx +++ b/packages/react/src/form/__tests__/form.test.tsx @@ -60,4 +60,16 @@ describe('
      ', () => { fireEvent.click(screen.getByText('submit')); expect(screen.getByText('username required')).toBeInTheDocument(); }); + + it('should forward ref to native form element', () => { + const ref = React.createRef(); + + render( + + + + ); + + expect(ref.current).toBeInstanceOf(HTMLFormElement); + }); }); diff --git a/packages/react/src/form/form.tsx b/packages/react/src/form/form.tsx index 64cd266f6..a57bf44f9 100644 --- a/packages/react/src/form/form.tsx +++ b/packages/react/src/form/form.tsx @@ -7,7 +7,7 @@ import { getPrefixCls } from '../_utils/general'; import { FormProps } from './types'; import FormInstance from './form-instance'; -const Form = (props: FormProps): JSX.Element => { +const Form = React.forwardRef((props, ref): JSX.Element => { const { initialValues = {}, labelCol = { span: 8, offset: 0 }, @@ -49,13 +49,13 @@ const Form = (props: FormProps): JSX.Element => { return ( -
      + {children}
      ); -}; +}); Form.displayName = 'Form'; diff --git a/packages/react/src/form/types.ts b/packages/react/src/form/types.ts index f3a9c5c38..b6294ccbb 100644 --- a/packages/react/src/form/types.ts +++ b/packages/react/src/form/types.ts @@ -66,7 +66,7 @@ export interface FormOptionsProps { export interface FormProps extends BaseProps, Partial, - React.PropsWithRef { + React.ComponentPropsWithoutRef<'form'> { form?: FormInstance; initialValues?: FormValues; onFinish?: (values: FormValues) => void; diff --git a/packages/react/src/grid/types.ts b/packages/react/src/grid/types.ts index 20edcc940..58aedf39d 100644 --- a/packages/react/src/grid/types.ts +++ b/packages/react/src/grid/types.ts @@ -11,7 +11,7 @@ export type RowJustify = | 'space-between' | 'space-evenly'; -export interface RowProps extends BaseProps, React.PropsWithRef { +export interface RowProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> { gutter?: number | [number, number]; gutterSide?: boolean; align?: RowAlign; @@ -29,7 +29,7 @@ export type GridTrackValue = number | React.CSSProperties['gridTemplateColumns'] export type GridItemSize = number | 'auto' | 'grow' | 'full'; export type GridItemOffset = number | 'auto'; -export interface GridProps extends BaseProps, React.PropsWithRef { +export interface GridProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> { columns?: ResponsiveValue; rows?: ResponsiveValue; spacing?: ResponsiveValue; @@ -51,7 +51,7 @@ export interface GridProps extends BaseProps, React.PropsWithRef { +export interface GridItemProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> { size?: ResponsiveValue; offset?: ResponsiveValue; column?: ResponsiveValue; @@ -64,7 +64,7 @@ export interface GridItemProps extends BaseProps, React.PropsWithRef { +export interface ColProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> { span?: number; offset?: number; order?: number; diff --git a/packages/react/src/image/types.ts b/packages/react/src/image/types.ts index e51e28a7c..0a49f01e7 100644 --- a/packages/react/src/image/types.ts +++ b/packages/react/src/image/types.ts @@ -3,7 +3,7 @@ import { BaseProps } from '../_utils/props'; export interface ImageProps extends BaseProps, - Omit, 'placeholder'> { + Omit, 'placeholder'> { src?: string; placeholder?: React.ReactNode; alt?: string; diff --git a/packages/react/src/input/types.ts b/packages/react/src/input/types.ts index 4dfc34d1e..b41bd5ac6 100644 --- a/packages/react/src/input/types.ts +++ b/packages/react/src/input/types.ts @@ -3,7 +3,7 @@ import { BaseProps, SizeType } from '../_utils/props'; export interface InputProps extends BaseProps, - Omit, 'size' | 'prefix'> { + Omit, 'size' | 'prefix'> { clearable?: boolean; prefix?: ReactNode; suffix?: ReactNode; @@ -19,7 +19,7 @@ export interface InputProps export interface InputGroupProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'div'> { size?: SizeType; disabled?: boolean; children: React.ReactNode; @@ -27,7 +27,7 @@ export interface InputGroupProps export interface InputGroupAddonProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'div'> { noBorder?: boolean; disabled?: boolean; size?: SizeType; diff --git a/packages/react/src/keyboard/types.ts b/packages/react/src/keyboard/types.ts index d8e8476d5..3095ad101 100644 --- a/packages/react/src/keyboard/types.ts +++ b/packages/react/src/keyboard/types.ts @@ -1,6 +1,6 @@ import React from 'react'; import { BaseProps } from '../_utils/props'; -export interface KeyboardProps extends BaseProps, React.PropsWithRef { +export interface KeyboardProps extends BaseProps, React.ComponentPropsWithoutRef<'kbd'> { children?: React.ReactNode; } diff --git a/packages/react/src/layout/types.ts b/packages/react/src/layout/types.ts index 88a945c47..6572a9661 100644 --- a/packages/react/src/layout/types.ts +++ b/packages/react/src/layout/types.ts @@ -1,11 +1,11 @@ import React from 'react'; import { BaseProps } from '../_utils/props'; -export interface LayoutProps extends BaseProps, React.PropsWithRef {} +export interface LayoutProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> {} export type SidebarTheme = 'light' | 'dark'; -export interface SidebarProps extends BaseProps, React.PropsWithRef { +export interface SidebarProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> { collapsible?: boolean; collapsed?: boolean; defaultCollapsed?: boolean; diff --git a/packages/react/src/link/__tests__/link.test.tsx b/packages/react/src/link/__tests__/link.test.tsx index 1fadea1b0..ead4855c1 100644 --- a/packages/react/src/link/__tests__/link.test.tsx +++ b/packages/react/src/link/__tests__/link.test.tsx @@ -24,4 +24,18 @@ describe('', () => { const { container } = render(Link); expect(container.firstChild).toHaveClass('ty-link_disabled'); }); + + it('should forward ref to anchor element', () => { + const ref = React.createRef(); + render(Link); + + expect(ref.current).toBeInstanceOf(HTMLAnchorElement); + }); + + it('should forward ref to disabled fallback element', () => { + const ref = React.createRef(); + render(Link); + + expect(ref.current).toBeInstanceOf(HTMLSpanElement); + }); }); diff --git a/packages/react/src/link/link.tsx b/packages/react/src/link/link.tsx index f1abb9045..7116eb073 100755 --- a/packages/react/src/link/link.tsx +++ b/packages/react/src/link/link.tsx @@ -4,43 +4,47 @@ import { ConfigContext } from '../config-provider/config-context'; import { getPrefixCls } from '../_utils/general'; import { LinkProps } from './types'; -const Link = React.memo((props: LinkProps): React.ReactElement => { - const { - disabled = false, - external = true, - underline = true, - className, - style, - children, - target, - prefixCls: customisedCls, - ...otherProps - } = props; - const configContext = useContext(ConfigContext); - const prefixCls = getPrefixCls('link', configContext.prefixCls, customisedCls); - const cls = classNames(prefixCls, className, { - [`${prefixCls}_disabled`]: disabled, - [`${prefixCls}_no-underline`]: !underline, - }); +const Link = React.memo( + React.forwardRef((props, ref): React.ReactElement => { + const { + disabled = false, + external = true, + underline = true, + className, + style, + children, + target, + prefixCls: customisedCls, + ...otherProps + } = props; + const configContext = useContext(ConfigContext); + const prefixCls = getPrefixCls('link', configContext.prefixCls, customisedCls); + const cls = classNames(prefixCls, className, { + [`${prefixCls}_disabled`]: disabled, + [`${prefixCls}_no-underline`]: !underline, + }); + + if (disabled) { + return ( + } className={cls} style={style} role="link" aria-disabled="true"> + {children} + + ); + } - if (disabled) { return ( - + } + target={target ? target : external ? '_blank' : '_self'} + className={cls} + style={style} + role="link"> {children} - + ); - } - return ( - - {children} - - ); -}); + }) +); Link.displayName = 'Link'; diff --git a/packages/react/src/link/types.ts b/packages/react/src/link/types.ts index 5c24054a3..7dd5c3d8c 100644 --- a/packages/react/src/link/types.ts +++ b/packages/react/src/link/types.ts @@ -1,7 +1,7 @@ import React from 'react'; import { BaseProps } from '../_utils/props'; -export interface LinkProps extends BaseProps, React.PropsWithRef { +export interface LinkProps extends BaseProps, React.ComponentPropsWithoutRef<'a'> { external?: boolean; disabled?: boolean; underline?: boolean; diff --git a/packages/react/src/list/types.ts b/packages/react/src/list/types.ts index 66e9a3737..30da41595 100644 --- a/packages/react/src/list/types.ts +++ b/packages/react/src/list/types.ts @@ -4,7 +4,7 @@ import { PaginationProps } from '../pagination/types'; export interface ListProps extends BaseProps, - Omit, 'children'> { + Omit, 'children'> { dataSource?: T[]; renderItem?: (item: T, index: number) => React.ReactNode; header?: React.ReactNode; @@ -36,7 +36,7 @@ export interface ListPaginationProps extends Pick { + React.ComponentPropsWithoutRef<'li'> { extra?: React.ReactNode; actions?: React.ReactNode[]; children?: React.ReactNode; @@ -44,7 +44,7 @@ export interface ListItemProps export interface ListItemMetaProps extends BaseProps, - Omit, 'title'> { + Omit, 'title'> { avatar?: React.ReactNode; title?: React.ReactNode; description?: React.ReactNode; diff --git a/packages/react/src/loader/types.ts b/packages/react/src/loader/types.ts index 7c3c4b463..92839d18e 100644 --- a/packages/react/src/loader/types.ts +++ b/packages/react/src/loader/types.ts @@ -3,7 +3,7 @@ import { BaseProps, SizeType } from '../_utils/props'; export interface LoaderProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'div'> { /** customise the spinning indicator */ indicator?: ReactNode; diff --git a/packages/react/src/loading-bar/types.ts b/packages/react/src/loading-bar/types.ts index 89face8d2..6bb950005 100644 --- a/packages/react/src/loading-bar/types.ts +++ b/packages/react/src/loading-bar/types.ts @@ -3,7 +3,7 @@ import { BaseProps } from '../_utils/props'; export interface LoadingBarProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'div'> { didMount?: () => void; children?: React.ReactNode; } diff --git a/packages/react/src/marquee/types.ts b/packages/react/src/marquee/types.ts index fec42121d..ada77dbb2 100644 --- a/packages/react/src/marquee/types.ts +++ b/packages/react/src/marquee/types.ts @@ -3,7 +3,7 @@ import { BaseProps } from '../_utils/props'; export interface MarqueeProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'div'> { /** Scroll direction */ direction?: 'left' | 'right'; diff --git a/packages/react/src/menu/types.ts b/packages/react/src/menu/types.ts index 140c4900e..3c7d814bc 100644 --- a/packages/react/src/menu/types.ts +++ b/packages/react/src/menu/types.ts @@ -15,7 +15,7 @@ export interface MenuSelectInfo { export interface MenuProps extends BaseProps, - Omit, 'onSelect'> { + Omit, 'onSelect'> { defaultIndex?: string; selectedKeys?: string[]; defaultSelectedKeys?: string[]; @@ -55,7 +55,7 @@ export interface MenuProps export interface MenuItemProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'li'> { index?: string; disabled?: boolean; danger?: boolean; @@ -66,14 +66,14 @@ export interface MenuItemProps export interface MenuItemGroupProps extends BaseProps, - Omit, 'title'> { + Omit, 'title'> { index?: string; title?: ReactNode; } export interface SubMenuProps extends BaseProps, - Omit, 'title'> { + Omit, 'title'> { title: ReactNode; index?: string; disabled?: boolean; diff --git a/packages/react/src/native-select/types.ts b/packages/react/src/native-select/types.ts index 0656ee0f3..e59aefd16 100644 --- a/packages/react/src/native-select/types.ts +++ b/packages/react/src/native-select/types.ts @@ -1,13 +1,13 @@ import React from 'react'; import { BaseProps, SizeType } from '../_utils/props'; -export type NativeSelectGroupProps = React.PropsWithRef; +export type NativeSelectGroupProps = React.ComponentPropsWithoutRef<'optgroup'>; -export type NativeSelectOptionProps = React.PropsWithRef; +export type NativeSelectOptionProps = React.ComponentPropsWithoutRef<'option'>; export interface NativeSelectProps extends BaseProps, - Omit, 'size'> { + Omit, 'size'> { size?: SizeType; children: | React.ReactElement diff --git a/packages/react/src/notification/types.ts b/packages/react/src/notification/types.ts index 0412b7bcc..3bbe99b70 100644 --- a/packages/react/src/notification/types.ts +++ b/packages/react/src/notification/types.ts @@ -5,7 +5,7 @@ export type NotificationType = 'success' | 'error' | 'warning' | 'info' | undefi export interface NotificationProps extends BaseProps, - Omit, 'title'> { + Omit, 'title'> { type: NotificationType; title?: ReactNode; description?: ReactNode; diff --git a/packages/react/src/pagination/types.ts b/packages/react/src/pagination/types.ts index e211ff148..ec3c6929b 100644 --- a/packages/react/src/pagination/types.ts +++ b/packages/react/src/pagination/types.ts @@ -6,7 +6,7 @@ export type PaginationSize = 'sm' | 'md'; export interface PaginationProps extends BaseProps, - Omit, 'onChange'> { + Omit, 'onChange'> { current?: number; total?: number; defaultCurrent?: number; diff --git a/packages/react/src/popup/types.ts b/packages/react/src/popup/types.ts index 60d9414a3..7672386ff 100644 --- a/packages/react/src/popup/types.ts +++ b/packages/react/src/popup/types.ts @@ -17,7 +17,7 @@ export type Placement = | 'right' | 'right-end'; -export interface PopupProps extends BaseProps, Omit, 'children' | 'content'> { +export interface PopupProps extends BaseProps, Omit, 'children' | 'content'> { disabled?: boolean; content?: React.ReactNode; placement?: Placement; diff --git a/packages/react/src/progress/types.ts b/packages/react/src/progress/types.ts index a6dfa91ef..3b4cfe3a3 100644 --- a/packages/react/src/progress/types.ts +++ b/packages/react/src/progress/types.ts @@ -7,7 +7,7 @@ export const strokePresetColors = ['primary', 'blue', 'green', 'yellow', 'red']; export type BarBackgroundType = 'impulse' | 'striped'; -export interface BarProps extends BaseProps, React.PropsWithoutRef { +export interface BarProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> { percent?: number; /** Customise label style for both outer and inner label */ format?: (percent: number) => React.ReactNode; @@ -24,7 +24,7 @@ export interface BarProps extends BaseProps, React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'div'> { percent?: number; /** Customise label style for both outer and inner label */ format?: (percent: number) => React.ReactNode; diff --git a/packages/react/src/radio/radio-group.tsx b/packages/react/src/radio/radio-group.tsx index 7b2a42895..65f4a7d71 100755 --- a/packages/react/src/radio/radio-group.tsx +++ b/packages/react/src/radio/radio-group.tsx @@ -5,52 +5,50 @@ import { getPrefixCls } from '../_utils/general'; import { RadioGroupContext } from './radio-group-context'; import { RadioGroupProps } from './types'; -const RadioGroup = React.forwardRef( - (props: RadioGroupProps, ref): JSX.Element => { - const { - defaultValue = '', - disabled = false, - name, - onChange, - className, - children, - prefixCls: customisedCls, - ...otherProps - } = props; - const configContext = useContext(ConfigContext); - const prefixCls = getPrefixCls('radio-group', configContext.prefixCls, customisedCls); - const cls = classNames(prefixCls, className); - const [value, setValue] = useState( - 'value' in props ? (props.value as number | string) : defaultValue - ); +const RadioGroup = (props: RadioGroupProps): JSX.Element => { + const { + defaultValue = '', + disabled = false, + name, + onChange, + className, + children, + prefixCls: customisedCls, + ...otherProps + } = props; + const configContext = useContext(ConfigContext); + const prefixCls = getPrefixCls('radio-group', configContext.prefixCls, customisedCls); + const cls = classNames(prefixCls, className); + const [value, setValue] = useState( + 'value' in props ? (props.value as number | string) : defaultValue + ); - const radioOnChange = (e: React.ChangeEvent): void => { - if (!disabled) { - const val = e.currentTarget.value; - !('value' in props) && setValue(val); - onChange && onChange(val); - } - }; + const radioOnChange = (e: React.ChangeEvent): void => { + if (!disabled) { + const val = e.currentTarget.value; + !('value' in props) && setValue(val); + onChange && onChange(val); + } + }; - useEffect(() => { - 'value' in props && setValue(props.value as number | string); - }, [props]); + useEffect(() => { + 'value' in props && setValue(props.value as number | string); + }, [props]); - return ( - -
      - {children} -
      -
      - ); - } -); + return ( + +
      + {children} +
      +
      + ); +}; RadioGroup.displayName = 'RadioGroup'; diff --git a/packages/react/src/radio/types.ts b/packages/react/src/radio/types.ts index 0b29f676b..89e27ce9a 100644 --- a/packages/react/src/radio/types.ts +++ b/packages/react/src/radio/types.ts @@ -3,7 +3,7 @@ import { BaseProps } from '../_utils/props'; export interface RadioProps extends BaseProps, - Omit, 'onChange'> { + Omit, 'onChange'> { radioRef?: RefObject; value?: string | number; name?: string; @@ -15,7 +15,7 @@ export interface RadioProps export interface RadioGroupProps extends BaseProps, - Omit, 'onChange'> { + Omit, 'onChange'> { name?: string; defaultValue?: number | string; value?: number | string; diff --git a/packages/react/src/rate/types.ts b/packages/react/src/rate/types.ts index 5c944eac9..720d82b5b 100644 --- a/packages/react/src/rate/types.ts +++ b/packages/react/src/rate/types.ts @@ -3,7 +3,7 @@ import { ReactNode } from 'react'; import { BaseProps } from '../_utils/props'; export interface RateProps extends BaseProps, - Omit, 'onChange'> { + Omit, 'onChange'> { color?: string; clearable?: boolean; half?: boolean; diff --git a/packages/react/src/result/types.ts b/packages/react/src/result/types.ts index 9f80b0154..067335282 100644 --- a/packages/react/src/result/types.ts +++ b/packages/react/src/result/types.ts @@ -5,7 +5,7 @@ export type ResultStatus = 'success' | 'error' | 'info' | 'warning' | 'loading'; export interface ResultProps extends BaseProps, - Omit, 'title'> { + Omit, 'title'> { title?: ReactNode; subtitle?: ReactNode; status?: ResultStatus; diff --git a/packages/react/src/scroll-indicator/types.ts b/packages/react/src/scroll-indicator/types.ts index 0e8d5dc22..cd3465b81 100644 --- a/packages/react/src/scroll-indicator/types.ts +++ b/packages/react/src/scroll-indicator/types.ts @@ -4,7 +4,7 @@ import { Target } from '../_utils/dom'; export interface ScrollIndicatorProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'div'> { fixed?: boolean; target?: () => Target; } diff --git a/packages/react/src/scroll-number/types.ts b/packages/react/src/scroll-number/types.ts index 78b8cce39..d1bcf9293 100644 --- a/packages/react/src/scroll-number/types.ts +++ b/packages/react/src/scroll-number/types.ts @@ -3,7 +3,7 @@ import { BaseProps } from '../_utils/props'; export interface ScrollNumberProps extends BaseProps, - Omit, 'title' | 'prefix'> { + Omit, 'title' | 'prefix'> { /** The numeric value to display */ value?: number | string; /** Title displayed above the value */ diff --git a/packages/react/src/segmented/types.ts b/packages/react/src/segmented/types.ts index 766b5436e..78d0903c9 100644 --- a/packages/react/src/segmented/types.ts +++ b/packages/react/src/segmented/types.ts @@ -15,7 +15,7 @@ export type SegmentedValue = string | number; export interface SegmentedProps extends BaseProps, Omit< - React.PropsWithoutRef, + React.ComponentPropsWithoutRef<'div'>, 'children' | 'defaultValue' | 'onChange' > { options: SegmentedOption[]; diff --git a/packages/react/src/select/types.ts b/packages/react/src/select/types.ts index b6740c1c3..c1d5243cf 100644 --- a/packages/react/src/select/types.ts +++ b/packages/react/src/select/types.ts @@ -3,14 +3,14 @@ import { BaseProps, SizeType } from '../_utils/props'; export interface SelectOptGroupProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'li'> { label?: string; children?: React.ReactNode; } export interface SelectOptionsProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'li'> { value: string; label?: React.ReactNode; disabled?: boolean; @@ -28,7 +28,7 @@ export type SelectValue = string | string[]; export interface SelectProps extends BaseProps, Omit< - React.PropsWithoutRef, + React.ComponentPropsWithoutRef<'div'>, 'onSelect' | 'onChange' | 'defaultValue' > { value?: SelectValue; diff --git a/packages/react/src/skeleton/types.ts b/packages/react/src/skeleton/types.ts index 2d722cbc2..73588b73c 100644 --- a/packages/react/src/skeleton/types.ts +++ b/packages/react/src/skeleton/types.ts @@ -1,7 +1,7 @@ import React from 'react'; import { BaseProps } from '../_utils/props'; -export interface SkeletonProps extends BaseProps, React.PropsWithRef { +export interface SkeletonProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> { active?: boolean; rounded?: boolean; children?: React.ReactNode; diff --git a/packages/react/src/slider/types.ts b/packages/react/src/slider/types.ts index 005254e16..71492f7dc 100644 --- a/packages/react/src/slider/types.ts +++ b/packages/react/src/slider/types.ts @@ -14,7 +14,7 @@ export type SliderMarks = { export interface SliderProps extends BaseProps, - Omit, 'onChange' | 'defaultValue'> { + Omit, 'onChange' | 'defaultValue'> { value?: SliderValue; defaultValue?: SliderValue; min?: number; diff --git a/packages/react/src/space/types.ts b/packages/react/src/space/types.ts index 72a86fd2d..4fb1139b1 100644 --- a/packages/react/src/space/types.ts +++ b/packages/react/src/space/types.ts @@ -4,7 +4,7 @@ import { BaseProps, DirectionType, SizeType } from '../_utils/props'; export type SpaceAlign = 'start' | 'end' | 'center' | 'baseline'; export type SpaceSize = SizeType | number; -export interface SpaceProps extends BaseProps, React.PropsWithRef { +export interface SpaceProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> { align?: SpaceAlign; direction?: DirectionType; size?: SpaceSize; diff --git a/packages/react/src/speed-dial/types.ts b/packages/react/src/speed-dial/types.ts index 54f5c5ca4..ec17b0a83 100644 --- a/packages/react/src/speed-dial/types.ts +++ b/packages/react/src/speed-dial/types.ts @@ -6,7 +6,7 @@ export type SpeedDialTrigger = 'hover' | 'click'; export interface SpeedDialProps extends BaseProps, - Omit, 'children'> { + Omit, 'children'> { icon?: React.ReactNode; openIcon?: React.ReactNode; direction?: SpeedDialDirection; @@ -20,7 +20,7 @@ export interface SpeedDialProps export interface SpeedDialActionProps extends BaseProps, - Omit, 'children'> { + Omit, 'children'> { icon: React.ReactNode; tooltip?: string; tooltipPlacement?: 'left' | 'right' | 'top' | 'bottom'; diff --git a/packages/react/src/split/resizer.tsx b/packages/react/src/split/resizer.tsx index 48ea1e438..eef47a943 100644 --- a/packages/react/src/split/resizer.tsx +++ b/packages/react/src/split/resizer.tsx @@ -7,7 +7,7 @@ import { SplitSeparatorRenderProps } from './types'; export interface ResizerProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'div'> { visualSize?: number; hitAreaSize: number; orientation: DirectionType; diff --git a/packages/react/src/split/types.ts b/packages/react/src/split/types.ts index 5c09bfb8d..5cc56ea07 100644 --- a/packages/react/src/split/types.ts +++ b/packages/react/src/split/types.ts @@ -18,7 +18,7 @@ export type SplitSeparatorRenderProps = { export interface SplitPaneProps extends BaseProps, - Omit, 'children'> { + Omit, 'children'> { /** Minimum pane size */ min?: SplitSize; @@ -30,7 +30,7 @@ export interface SplitPaneProps export interface SplitProps extends BaseProps, - Omit, 'children'> { + Omit, 'children'> { /** Pane arrangement: horizontal => left/right, vertical => top/bottom */ orientation?: SplitOrientation; diff --git a/packages/react/src/statistic/types.ts b/packages/react/src/statistic/types.ts index 782a0b8d5..612f0829f 100644 --- a/packages/react/src/statistic/types.ts +++ b/packages/react/src/statistic/types.ts @@ -49,7 +49,7 @@ export interface StatisticStatus { export interface StatisticProps extends BaseProps, - Omit, 'title' | 'prefix'> { + Omit, 'title' | 'prefix'> { title?: React.ReactNode; description?: React.ReactNode; tooltip?: React.ReactNode; diff --git a/packages/react/src/steps/types.ts b/packages/react/src/steps/types.ts index 3da660f89..43ea3131f 100644 --- a/packages/react/src/steps/types.ts +++ b/packages/react/src/steps/types.ts @@ -5,7 +5,7 @@ export type StepsStatus = 'wait' | 'process' | 'finish' | 'error'; export interface StepsProps extends BaseProps, - Omit, 'onChange'> { + Omit, 'onChange'> { current?: number; defaultCurrent?: number; direction?: DirectionType; @@ -16,7 +16,7 @@ export interface StepsProps export interface StepsItemProps extends BaseProps, - Omit, 'title'> { + Omit, 'title'> { stepIndex?: number; icon?: ReactNode; title?: ReactNode; diff --git a/packages/react/src/sticky/types.ts b/packages/react/src/sticky/types.ts index feabb7b25..f210abfd8 100644 --- a/packages/react/src/sticky/types.ts +++ b/packages/react/src/sticky/types.ts @@ -4,7 +4,7 @@ import { Target } from '../_utils/dom'; export interface StickyProps extends BaseProps, - Omit, 'onChange'> { + Omit, 'onChange'> { offsetTop?: number; offsetBottom?: number; container?: () => Target; diff --git a/packages/react/src/strength-indicator/types.ts b/packages/react/src/strength-indicator/types.ts index 0d3ec3089..a2eba9865 100644 --- a/packages/react/src/strength-indicator/types.ts +++ b/packages/react/src/strength-indicator/types.ts @@ -3,7 +3,7 @@ import { BaseProps } from '../_utils/props'; export interface StrengthIndicatorProps extends BaseProps, - React.PropsWithRef { + React.ComponentPropsWithoutRef<'div'> { /** the number of indicator, default is 3 */ blocks?: number; diff --git a/packages/react/src/switch/types.ts b/packages/react/src/switch/types.ts index 1bc72ed12..194654265 100644 --- a/packages/react/src/switch/types.ts +++ b/packages/react/src/switch/types.ts @@ -3,7 +3,7 @@ import { BaseProps, SizeType } from '../_utils/props'; export interface SwitchProps extends BaseProps, - Omit, 'onChange' | 'onClick'> { + Omit, 'onChange' | 'onClick'> { defaultChecked?: boolean; checked?: boolean; disabled?: boolean; diff --git a/packages/react/src/table/types.ts b/packages/react/src/table/types.ts index 3ebc209e5..264b1f8e6 100644 --- a/packages/react/src/table/types.ts +++ b/packages/react/src/table/types.ts @@ -33,7 +33,7 @@ export interface TablePaginationConfig extends Pick extends BaseProps, - Omit, 'onChange'> { + Omit, 'onChange'> { columns: ColumnType[]; dataSource?: T[]; rowKey?: string | ((record: T) => React.Key); diff --git a/packages/react/src/tag/types.ts b/packages/react/src/tag/types.ts index 0d23ab002..afbc0b364 100644 --- a/packages/react/src/tag/types.ts +++ b/packages/react/src/tag/types.ts @@ -29,7 +29,7 @@ export const PresetColors = [ ...StatusColors, ]; -export interface TagProps extends BaseProps, React.PropsWithoutRef { +export interface TagProps extends BaseProps, React.ComponentPropsWithoutRef<'div'> { color?: string | StatusColor; variant?: TagVariant; closable?: boolean; diff --git a/packages/react/src/text-loop/types.ts b/packages/react/src/text-loop/types.ts index dc2c6457c..3299fd805 100644 --- a/packages/react/src/text-loop/types.ts +++ b/packages/react/src/text-loop/types.ts @@ -3,7 +3,7 @@ import { BaseProps } from '../_utils/props'; export interface TextLoopProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'div'> { /** Time each item stays visible, in ms (default: 3000) */ interval?: number; /** Pause cycling on hover (default: true) */ diff --git a/packages/react/src/timeline/types.ts b/packages/react/src/timeline/types.ts index 38b176280..3d310b4e5 100644 --- a/packages/react/src/timeline/types.ts +++ b/packages/react/src/timeline/types.ts @@ -5,14 +5,14 @@ export type TimelinePosition = 'left' | 'center'; export interface TimelineProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'ul'> { position?: TimelinePosition; children: React.ReactNode; } export interface TimelineItemProps extends BaseProps, - React.PropsWithoutRef { + React.ComponentPropsWithoutRef<'li'> { dot?: React.ReactNode; dotStyle?: CSSProperties; children?: React.ReactNode; diff --git a/packages/react/src/transfer/transfer-panel.tsx b/packages/react/src/transfer/transfer-panel.tsx index 4a92252c7..f3f077d30 100644 --- a/packages/react/src/transfer/transfer-panel.tsx +++ b/packages/react/src/transfer/transfer-panel.tsx @@ -11,7 +11,7 @@ import Input from '../input/input'; export interface TransferPanelProps extends BaseProps, - Omit, 'title' | 'onChange'> { + Omit, 'title' | 'onChange'> { dataSource: TransferItem[]; checkedKeys: string[]; onChange: (checkedKeys: string[]) => void; diff --git a/packages/react/src/transfer/types.ts b/packages/react/src/transfer/types.ts index 949594b62..1b0e36ca9 100644 --- a/packages/react/src/transfer/types.ts +++ b/packages/react/src/transfer/types.ts @@ -9,7 +9,7 @@ export type TransferItem = { export interface TransferProps extends BaseProps, - Omit, 'onChange'> { + Omit, 'onChange'> { dataSource?: TransferItem[]; value?: string[]; defaultValue?: string[]; diff --git a/packages/react/src/tree/types.ts b/packages/react/src/tree/types.ts index f22384022..6120f0d9c 100644 --- a/packages/react/src/tree/types.ts +++ b/packages/react/src/tree/types.ts @@ -12,7 +12,7 @@ export type TreeData = { export interface TreeProps extends BaseProps, - Omit, 'onSelect'> { + Omit, 'onSelect'> { data?: TreeData[]; indent?: number; checkable?: boolean; diff --git a/packages/react/src/typography/types.ts b/packages/react/src/typography/types.ts index 0797e80bf..d236215f2 100644 --- a/packages/react/src/typography/types.ts +++ b/packages/react/src/typography/types.ts @@ -30,11 +30,11 @@ export interface TypographyCopyableConfig { export interface TypographyProps extends BaseProps, - React.PropsWithRef { + React.ComponentPropsWithoutRef<'div'> { children?: React.ReactNode; } -export interface ParagraphProps extends BaseProps, React.PropsWithRef { +export interface ParagraphProps extends BaseProps, React.ComponentPropsWithoutRef<'p'> { as?: TypographyParagraphTag; ellipsis?: boolean | TypographyEllipsisConfig; children?: React.ReactNode; @@ -42,12 +42,12 @@ export interface ParagraphProps extends BaseProps, React.PropsWithRef> { + React.HTMLAttributes { level?: 1 | 2 | 3 | 4 | 5 | 6; children?: React.ReactNode; } -export interface TextProps extends BaseProps, React.PropsWithRef { +export interface TextProps extends BaseProps, React.ComponentPropsWithoutRef<'span'> { as?: TypographyTextTag; type?: TextType; copyable?: boolean | TypographyCopyableConfig; diff --git a/packages/react/src/waterfall/types.ts b/packages/react/src/waterfall/types.ts index bf23b34e2..b46fda252 100644 --- a/packages/react/src/waterfall/types.ts +++ b/packages/react/src/waterfall/types.ts @@ -15,7 +15,7 @@ export interface WaterfallItem { export interface WaterfallProps extends BaseProps, - Omit, 'children'> { + Omit, 'children'> { /** Number of columns, or responsive breakpoint config. Default: 3 */ columns?: number | Partial>; /** Spacing between items: number or [horizontal, vertical] */