refactor: All the warning set the warning type for future filter (#44613)

* feat: add warningContext

* refactor: part refactor

* chore: fix compile

* chore: part of it

* chore: part of it

* chore: part of it

* chore: fix lint

* chore: fix test

* chore: clean uo

* chore: hide warning def tmp

* chore: comment test

* chore: fix lint

* chore: refactor select icons

* chore: fix warning message

* test: update test

* chore: rm dead code
This commit is contained in:
二货爱吃白萝卜 2023-09-11 17:28:04 +08:00 committed by GitHub
parent 3596c8811e
commit 5cc338e177
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 903 additions and 417 deletions

View File

@ -1,3 +1,4 @@
import * as React from 'react';
import rcWarning, { resetWarned } from 'rc-util/lib/warning';
export { resetWarned };
@ -18,4 +19,42 @@ if (process.env.NODE_ENV !== 'production') {
};
}
type TypeWarning = (
valid: boolean,
component: string,
/**
* - deprecated: Some API will be removed in future but still support now.
* - usage: Some API usage is not correct.
* - breaking: Breaking change like API is removed.
*/
type: 'deprecated' | 'usage' | 'breaking',
message?: string,
) => void;
export interface WarningContextProps {
deprecated?: boolean;
}
export const WarningContext = React.createContext<WarningContextProps>({});
/**
* This is a hook but we not named as `useWarning`
* since this is only used in development.
* We should always wrap this in `if (process.env.NODE_ENV !== 'production')` condition
*/
export const devUseWarning: () => TypeWarning =
process.env.NODE_ENV !== 'production'
? () => {
const { deprecated } = React.useContext(WarningContext);
const typeWarning: TypeWarning = (valid, component, type, message) => {
if (deprecated !== false || type !== 'deprecated') {
warning(valid, component, message);
}
};
return typeWarning;
}
: () => noop;
export default warning;

View File

@ -1,3 +1,5 @@
import type { ReactElement } from 'react';
import * as React from 'react';
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
@ -6,12 +8,10 @@ import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled';
import classNames from 'classnames';
import CSSMotion from 'rc-motion';
import pickAttrs from 'rc-util/lib/pickAttrs';
import type { ReactElement } from 'react';
import * as React from 'react';
import { replaceElement } from '../_util/reactNode';
import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import { replaceElement } from '../_util/reactNode';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
// CSSINJS
import useStyle from './style';
@ -118,8 +118,16 @@ const Alert: React.FC<AlertProps> = (props) => {
} = props;
const [closed, setClosed] = React.useState(false);
if (process.env.NODE_ENV !== 'production') {
warning(!closeText, 'Alert', '`closeText` is deprecated. Please use `closeIcon` instead.');
const warning = devUseWarning();
warning(
!closeText,
'Alert',
'deprecated',
'`closeText` is deprecated. Please use `closeIcon` instead.',
);
}
const ref = React.useRef<HTMLDivElement>(null);
const { getPrefixCls, direction, alert } = React.useContext(ConfigContext);

View File

@ -1,18 +1,17 @@
import * as React from 'react';
import classNames from 'classnames';
import { useEvent } from 'rc-util';
import * as React from 'react';
import scrollIntoView from 'scroll-into-view-if-needed';
import getScroll from '../_util/getScroll';
import scrollTo from '../_util/scrollTo';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import Affix from '../affix';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import type { AnchorLinkBaseProps } from './AnchorLink';
import AnchorLink from './AnchorLink';
import AnchorContext from './context';
import useStyle from './style';
export interface AnchorLinkItemProps extends AnchorLinkBaseProps {
@ -133,13 +132,19 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
warning(!children, 'Anchor', '`Anchor children` is deprecated. Please use `items` instead.');
}
const warning = devUseWarning();
warning(
!children,
'Anchor',
'deprecated',
'`Anchor children` is deprecated. Please use `items` instead.',
);
if (process.env.NODE_ENV !== 'production') {
warning(
!(anchorDirection === 'horizontal' && items?.some((n) => 'children' in n)),
'Anchor',
'usage',
'`Anchor items#children` is not supported when `Anchor` direction is horizontal.',
);
}

View File

@ -1,6 +1,7 @@
import classNames from 'classnames';
import * as React from 'react';
import warning from '../_util/warning';
import classNames from 'classnames';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { AntAnchor } from './Anchor';
import AnchorContext from './context';
@ -51,9 +52,12 @@ const AnchorLink: React.FC<AnchorLinkProps> = (props) => {
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!children || direction !== 'horizontal',
'Anchor.Link',
'usage',
'`Anchor.Link children` is not supported when `Anchor` direction is horizontal',
);
}

View File

@ -1,12 +1,13 @@
import * as React from 'react';
import classNames from 'classnames';
import type { BaseSelectRef } from 'rc-select';
import toArray from 'rc-util/lib/Children/toArray';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import genPurePanel from '../_util/PurePanel';
import { isValidElement } from '../_util/reactNode';
import type { InputStatus } from '../_util/statusUtils';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import type {
@ -33,6 +34,7 @@ export interface AutoCompleteProps<
InternalSelectProps<ValueType, OptionType>,
'loading' | 'mode' | 'optionLabelProp' | 'labelInValue'
> {
/** @deprecated Please use `options` instead */
dataSource?: DataSourceItemType[];
status?: InputStatus;
popupClassName?: string;
@ -102,11 +104,6 @@ const AutoComplete: React.ForwardRefRenderFunction<RefSelectProps, AutoCompleteP
);
}
default:
warning(
false,
'AutoComplete',
'`dataSource` is only supports type `string[] | Object[]`.',
);
return undefined;
}
})
@ -114,21 +111,26 @@ const AutoComplete: React.ForwardRefRenderFunction<RefSelectProps, AutoCompleteP
}
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!('dataSource' in props),
'AutoComplete',
'deprecated',
'`dataSource` is deprecated, please use `options` instead.',
);
warning(
!customizeInput || !('size' in props),
'AutoComplete',
'usage',
'You need to control style self instead of setting `size` when using customize input.',
);
warning(
!dropdownClassName,
'AutoComplete',
'deprecated',
'`dropdownClassName` is deprecated, please use `popupClassName` instead.',
);
}

View File

@ -1,16 +1,17 @@
import * as React from 'react';
import classNames from 'classnames';
import ResizeObserver from 'rc-resize-observer';
import { composeRef } from 'rc-util/lib/ref';
import * as React from 'react';
import type { Breakpoint } from '../_util/responsiveObserver';
import { responsiveArray } from '../_util/responsiveObserver';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import useSize from '../config-provider/hooks/useSize';
import useBreakpoint from '../grid/hooks/useBreakpoint';
import type { AvatarContextType, AvatarSize } from './AvatarContext';
import AvatarContext from './AvatarContext';
import useStyle from './style';
import useSize from '../config-provider/hooks/useSize';
export interface AvatarProps {
/** Shape of avatar, options: `circle`, `square` */
@ -132,11 +133,16 @@ const InternalAvatar: React.ForwardRefRenderFunction<HTMLSpanElement, AvatarProp
: {};
}, [screens, size]);
warning(
!(typeof icon === 'string' && icon.length > 2),
'Avatar',
`\`icon\` is using ReactNode instead of string naming in v4. Please check \`${icon}\` at https://ant.design/components/icon`,
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!(typeof icon === 'string' && icon.length > 2),
'Avatar',
'deprecated',
`\`icon\` is using ReactNode instead of string naming in v4. Please check \`${icon}\` at https://ant.design/components/icon`,
);
}
const prefixCls = getPrefixCls('avatar', customizePrefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls);

View File

@ -1,13 +1,14 @@
import * as React from 'react';
import VerticalAlignTopOutlined from '@ant-design/icons/VerticalAlignTopOutlined';
import classNames from 'classnames';
import CSSMotion from 'rc-motion';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import getScroll from '../_util/getScroll';
import { cloneElement } from '../_util/reactNode';
import scrollTo from '../_util/scrollTo';
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import useStyle from './style';
@ -49,7 +50,14 @@ const BackTop: React.FC<BackTopProps> = (props) => {
);
if (process.env.NODE_ENV !== 'production') {
warning(false, 'BackTop', '`BackTop` is deprecated, please use `FloatButton.BackTop` instead.');
const warning = devUseWarning();
warning(
false,
'BackTop',
'deprecated',
'`BackTop` is deprecated, please use `FloatButton.BackTop` instead.',
);
}
React.useEffect(() => {

View File

@ -1,19 +1,19 @@
import * as React from 'react';
import classNames from 'classnames';
import toArray from 'rc-util/lib/Children/toArray';
import pickAttrs from 'rc-util/lib/pickAttrs';
import * as React from 'react';
import { cloneElement } from '../_util/reactNode';
import warning from '../_util/warning';
import type { AnyObject } from '../_util/type';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { DropdownProps } from '../dropdown';
import type { BreadcrumbItemProps } from './BreadcrumbItem';
import BreadcrumbItem, { InternalBreadcrumbItem } from './BreadcrumbItem';
import BreadcrumbSeparator from './BreadcrumbSeparator';
import type { DropdownProps } from '../dropdown';
import useStyle from './style';
import useItemRender from './useItemRender';
import useItems from './useItems';
import type { AnyObject } from '../_util/type';
export interface BreadcrumbItemType {
key?: React.Key;
@ -99,7 +99,39 @@ const Breadcrumb = <T extends AnyObject = AnyObject>(props: BreadcrumbProps<T>)
const mergedItems = useItems(items, legacyRoutes);
if (process.env.NODE_ENV !== 'production') {
warning(!legacyRoutes, 'Breadcrumb', '`routes` is deprecated. Please use `items` instead.');
const warning = devUseWarning();
warning(
!legacyRoutes,
'Breadcrumb',
'deprecated',
'`routes` is deprecated. Please use `items` instead.',
);
// Deprecated warning for breadcrumb children
if (!mergedItems || mergedItems.length === 0) {
const childList = toArray(children);
warning(
childList.length === 0,
'Breadcrumb',
'deprecated',
'`Breadcrumb.Item and Breadcrumb.Separator` is deprecated. Please use `items` instead.',
);
childList.forEach((element: any) => {
if (element) {
warning(
element.type &&
(element.type.__ANT_BREADCRUMB_ITEM === true ||
element.type.__ANT_BREADCRUMB_SEPARATOR === true),
'Breadcrumb',
'usage',
"Only accepts Breadcrumb.Item and Breadcrumb.Separator as it's children",
);
}
});
}
}
const mergedItemRender = useItemRender(prefixCls, itemRender);
@ -170,21 +202,7 @@ const Breadcrumb = <T extends AnyObject = AnyObject>(props: BreadcrumbProps<T>)
if (!element) {
return element;
}
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
warning(
!element,
'Breadcrumb',
'`Breadcrumb.Item and Breadcrumb.Separator` is deprecated. Please use `items` instead.',
);
}
warning(
element.type &&
(element.type.__ANT_BREADCRUMB_ITEM === true ||
element.type.__ANT_BREADCRUMB_SEPARATOR === true),
'Breadcrumb',
"Only accepts Breadcrumb.Item and Breadcrumb.Separator as it's children",
);
const isLastItem = index === childrenLength - 1;
return cloneElement(element, {
separator: isLastItem ? '' : separator,

View File

@ -1,6 +1,6 @@
import DownOutlined from '@ant-design/icons/DownOutlined';
import * as React from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { DropdownProps } from '../dropdown/dropdown';
import Dropdown from '../dropdown/dropdown';
@ -42,9 +42,12 @@ export const InternalBreadcrumbItem: React.FC<BreadcrumbItemProps> = (props) =>
// Warning for deprecated usage
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!('overlay' in props),
'Breadcrumb.Item',
'deprecated',
'`overlay` is deprecated. Please use `menu` instead.',
);
}

View File

@ -1,6 +1,7 @@
import classNames from 'classnames';
import * as React from 'react';
import warning from '../_util/warning';
import classNames from 'classnames';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { SizeType } from '../config-provider/SizeContext';
import { useToken } from '../theme/internal';
@ -33,10 +34,19 @@ const ButtonGroup: React.FC<ButtonGroupProps> = (props) => {
sizeCls = 'sm';
break;
case 'middle':
case undefined:
break;
default:
warning(!size, 'Button.Group', 'Invalid prop `size`.');
// Do nothing
}
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!size || ['large', 'small', 'middle'].includes(size),
'Button.Group',
'usage',
'Invalid prop `size`.',
);
}
const classes = classNames(

View File

@ -12,7 +12,7 @@ import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import { composeRef } from 'rc-util/lib/ref';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import Wave from '../_util/wave';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
@ -192,17 +192,23 @@ const InternalButton: React.ForwardRefRenderFunction<
(onClick as React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>)?.(e);
};
warning(
!(typeof icon === 'string' && icon.length > 2),
'Button',
`\`icon\` is using ReactNode instead of string naming in v4. Please check \`${icon}\` at https://ant.design/components/icon`,
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!(ghost && isUnBorderedButtonType(type)),
'Button',
"`link` or `text` button can't be a `ghost` button.",
);
warning(
!(typeof icon === 'string' && icon.length > 2),
'Button',
'breaking',
`\`icon\` is using ReactNode instead of string naming in v4. Please check \`${icon}\` at https://ant.design/components/icon`,
);
warning(
!(ghost && isUnBorderedButtonType(type)),
'Button',
'usage',
"`link` or `text` button can't be a `ghost` button.",
);
}
const autoInsertSpace = autoInsertSpaceInButton !== false;
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);

View File

@ -1,20 +1,20 @@
import * as React from 'react';
import classNames from 'classnames';
import { PickerPanel as RCPickerPanel } from 'rc-picker';
import type { GenerateConfig } from 'rc-picker/lib/generate';
import type { CellRenderInfo } from 'rc-picker/lib/interface';
import type {
PickerPanelBaseProps as RCPickerPanelBaseProps,
PickerPanelDateProps as RCPickerPanelDateProps,
PickerPanelTimeProps as RCPickerPanelTimeProps,
} from 'rc-picker/lib/PickerPanel';
import type { GenerateConfig } from 'rc-picker/lib/generate';
import type { CellRenderInfo } from 'rc-picker/lib/interface';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import * as React from 'react';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import { useLocale } from '../locale';
import CalendarHeader from './Header';
import enUS from './locale/en_US';
import warning from '../_util/warning';
import useStyle from './style';
type InjectDefaultProps<Props> = Omit<
@ -125,24 +125,30 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
// ====================== Warning =======================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!dateFullCellRender,
'Calendar',
'deprecated',
'`dateFullCellRender` is deprecated. Please use `fullCellRender` instead.',
);
warning(
!dateCellRender,
'Calendar',
'deprecated',
'`dateCellRender` is deprecated. Please use `cellRender` instead.',
);
warning(
!monthFullCellRender,
'Calendar',
'deprecated',
'`monthFullCellRender` is deprecated. Please use `fullCellRender` instead.',
);
warning(
!monthCellRender,
'Calendar',
'deprecated',
'`monthCellRender` is deprecated. Please use `cellRender` instead.',
);
}

View File

@ -20,7 +20,7 @@ import { getTransitionName } from '../_util/motion';
import genPurePanel from '../_util/PurePanel';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import DisabledContext from '../config-provider/DisabledContext';
@ -30,7 +30,7 @@ import { FormItemInputContext } from '../form/context';
import useSelectStyle from '../select/style';
import useBuiltinPlacements from '../select/useBuiltinPlacements';
import useShowArrow from '../select/useShowArrow';
import getIcons from '../select/utils/iconUtil';
import useIcons from '../select/useIcons';
import { useCompactItemContext } from '../space/Compact';
import useStyle from './style';
@ -194,15 +194,19 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!dropdownClassName,
'Cascader',
'deprecated',
'`dropdownClassName` is deprecated. Please use `popupClassName` instead.',
);
warning(
!('showArrow' in props),
'Cascader',
'deprecated',
'`showArrow` is deprecated which will be removed in next major version. It will be a default behavior, you can hide it by setting `suffixIcon` to null.',
);
}
@ -279,7 +283,7 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
// ===================== Icons =====================
const showSuffixIcon = useShowArrow(props.suffixIcon, showArrow);
const { suffixIcon, removeIcon, clearIcon } = getIcons({
const { suffixIcon, removeIcon, clearIcon } = useIcons({
...props,
hasFeedback,
feedbackIcon,

View File

@ -1,16 +1,16 @@
import * as React from 'react';
import classNames from 'classnames';
import type { CheckboxRef } from 'rc-checkbox';
import RcCheckbox from 'rc-checkbox';
import * as React from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import Wave from '../_util/wave';
import { TARGET_CLS } from '../_util/wave/interface';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import { FormItemInputContext } from '../form/context';
import GroupContext from './GroupContext';
import useStyle from './style';
import Wave from '../_util/wave';
import { TARGET_CLS } from '../_util/wave/interface';
export interface AbstractCheckboxProps<T> {
prefixCls?: string;
@ -77,13 +77,19 @@ const InternalCheckbox: React.ForwardRefRenderFunction<CheckboxRef, CheckboxProp
const prevValue = React.useRef(restProps.value);
React.useEffect(() => {
checkboxGroup?.registerValue(restProps.value);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
'checked' in restProps || !!checkboxGroup || !('value' in restProps),
'Checkbox',
'usage',
'`value` is not a valid prop, do you mean `checked`?',
);
}
React.useEffect(() => {
checkboxGroup?.registerValue(restProps.value);
}, []);
React.useEffect(() => {

View File

@ -1,3 +1,4 @@
import * as React from 'react';
import RightOutlined from '@ant-design/icons/RightOutlined';
import classNames from 'classnames';
import type { CollapseProps as RcCollapseProps } from 'rc-collapse';
@ -5,13 +6,13 @@ import RcCollapse from 'rc-collapse';
import type { CSSMotionProps } from 'rc-motion';
import toArray from 'rc-util/lib/Children/toArray';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import initCollapseMotion from '../_util/motion';
import { cloneElement } from '../_util/reactNode';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { SizeType } from '../config-provider/SizeContext';
import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext';
import type { CollapsibleType } from './CollapsePanel';
import CollapsePanel from './CollapsePanel';
import useStyle from './style';
@ -77,12 +78,17 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
const rootPrefixCls = getPrefixCls();
const [wrapSSR, hashId] = useStyle(prefixCls);
// Warning if use legacy type `expandIconPosition`
warning(
expandIconPosition !== 'left' && expandIconPosition !== 'right',
'Collapse',
'`expandIconPosition` with `left` or `right` is deprecated. Please use `start` or `end` instead.',
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
// Warning if use legacy type `expandIconPosition`
warning(
expandIconPosition !== 'left' && expandIconPosition !== 'right',
'Collapse',
'deprecated',
'`expandIconPosition` with `left` or `right` is deprecated. Please use `start` or `end` instead.',
);
}
// Align with logic position
const mergedExpandIconPosition = React.useMemo(() => {

View File

@ -1,7 +1,8 @@
import * as React from 'react';
import classNames from 'classnames';
import RcCollapse from 'rc-collapse';
import * as React from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
export type CollapsibleType = 'header' | 'icon' | 'disabled';
@ -23,11 +24,16 @@ export interface CollapsePanelProps {
}
const CollapsePanel = React.forwardRef<HTMLDivElement, CollapsePanelProps>((props, ref) => {
warning(
!('disabled' in props),
'Collapse.Panel',
'`disabled` is deprecated. Please use `collapsible="disabled"` instead.',
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!('disabled' in props),
'Collapse.Panel',
'deprecated',
'`disabled` is deprecated. Please use `collapsible="disabled"` instead.',
);
}
const { getPrefixCls } = React.useContext(ConfigContext);
const { prefixCls: customizePrefixCls, className, showArrow = true } = props;

View File

@ -9,7 +9,7 @@ import useMergedState from 'rc-util/lib/hooks/useMergedState';
import genPurePanel from '../_util/PurePanel';
import { getStatusClassNames } from '../_util/statusUtils';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import type { ConfigConsumerProps } from '../config-provider/context';
import { ConfigContext } from '../config-provider/context';
import useSize from '../config-provider/hooks/useSize';
@ -151,9 +151,12 @@ const ColorPicker: CompoundedComponent = (props) => {
// ===================== Warning ======================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!(disabledAlpha && isAlphaColor),
'ColorPicker',
'usage',
'`disabledAlpha` will make the alpha to be 100% when use alpha color.',
);
}

View File

@ -0,0 +1,30 @@
import * as React from 'react';
import { devUseWarning } from '../_util/warning';
export interface PropWarningProps {
dropdownMatchSelectWidth?: boolean;
}
/**
* Warning for ConfigProviderProps.
* This will be empty function in production.
*/
const PropWarning = React.memo(({ dropdownMatchSelectWidth }: PropWarningProps) => {
const warning = devUseWarning();
warning(
dropdownMatchSelectWidth === undefined,
'ConfigProvider',
'deprecated',
'`dropdownMatchSelectWidth` is deprecated. Please use `popupMatchSelectWidth` instead.',
);
return null;
});
if (process.env.NODE_ENV !== 'production') {
PropWarning.displayName = 'PropWarning';
}
export default process.env.NODE_ENV !== 'production' ? PropWarning : () => null;

View File

@ -1,5 +1,6 @@
import { SmileOutlined } from '@ant-design/icons';
import React, { useState } from 'react';
import { SmileOutlined } from '@ant-design/icons';
import type { ConfigConsumerProps } from '..';
import ConfigProvider, { ConfigContext } from '..';
import mountTest from '../../../tests/shared/mountTest';
@ -123,4 +124,13 @@ describe('ConfigProvider', () => {
expect(rendered).toBeTruthy();
expect(cacheRenderEmpty).toBeFalsy();
});
// it('warning support filter level', () => {
// resetWarned();
// const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
// render(<ConfigProvider dropdownMatchSelectWidth warning={{ deprecated: false }} />);
// expect(errSpy).not.toHaveBeenCalled();
// });
});

View File

@ -1,17 +1,19 @@
import type { ValidateMessages } from 'rc-field-form/lib/interface';
import * as React from 'react';
import type { ValidateMessages } from 'rc-field-form/lib/interface';
import type { Options } from 'scroll-into-view-if-needed';
import type { WarningContextProps } from '../_util/warning';
import type { ShowWaveEffect } from '../_util/wave/interface';
import type { BadgeProps } from '../badge';
import type { ButtonProps } from '../button';
import type { RequiredMark } from '../form/Form';
import type { InputProps } from '../input';
import type { Locale } from '../locale';
import type { SpaceProps } from '../space';
import type { AliasToken, MappingAlgorithm, OverrideToken } from '../theme/interface';
import type { SizeType } from './SizeContext';
import type { RenderEmptyHandler } from './defaultRenderEmpty';
import type { ShowWaveEffect } from '../_util/wave/interface';
import type { TabsProps } from '../tabs';
import type { AliasToken, MappingAlgorithm, OverrideToken } from '../theme/interface';
import type { RenderEmptyHandler } from './defaultRenderEmpty';
import type { SizeType } from './SizeContext';
export const defaultIconPrefixCls = 'anticon';
@ -154,6 +156,8 @@ export interface ConfigConsumerProps {
datePicker?: ComponentStyleConfig;
wave?: WaveConfig;
warning?: WarningContextProps;
}
const defaultGetPrefixCls = (suffixCls?: string, customizePrefixCls?: string) => {

View File

@ -1,11 +1,11 @@
import * as React from 'react';
import { createTheme } from '@ant-design/cssinjs';
import IconContext from '@ant-design/icons/lib/components/Context';
import type { ValidateMessages } from 'rc-field-form/lib/interface';
import useMemo from 'rc-util/lib/hooks/useMemo';
import { merge } from 'rc-util/lib/utils/set';
import type { ReactElement } from 'react';
import * as React from 'react';
import type { Options } from 'scroll-into-view-if-needed';
import warning from '../_util/warning';
import type { RequiredMark } from '../form/Form';
import ValidateMessagesContext from '../form/validateMessagesContext';
@ -16,6 +16,8 @@ import type { LocaleContextProps } from '../locale/context';
import LocaleContext from '../locale/context';
import defaultLocale from '../locale/en_US';
import type { SpaceProps } from '../space';
import type { TabsProps } from '../tabs';
import { defaultTheme } from '../theme/context';
import { DesignTokenContext } from '../theme/internal';
import defaultSeedToken from '../theme/themes/seed';
import type {
@ -37,11 +39,10 @@ import { DisabledContextProvider } from './DisabledContext';
import useConfig from './hooks/useConfig';
import useTheme from './hooks/useTheme';
import MotionWrapper from './MotionWrapper';
import PropWarning from './PropWarning';
import type { SizeType } from './SizeContext';
import SizeContext, { SizeContextProvider } from './SizeContext';
import useStyle from './style';
import { defaultTheme } from '../theme/context';
import type { TabsProps } from '../tabs';
/**
* Since too many feedback using static method like `Modal.confirm` not getting theme, we record the
@ -85,7 +86,10 @@ export const configConsumerProps = [
];
// These props is used by `useContext` directly in sub component
const PASSED_PROPS: Exclude<keyof ConfigConsumerProps, 'rootPrefixCls' | 'getPrefixCls'>[] = [
const PASSED_PROPS: Exclude<
keyof ConfigConsumerProps,
'rootPrefixCls' | 'getPrefixCls' | 'warning'
>[] = [
'getTargetContainer',
'getPopupContainer',
'renderEmpty',
@ -141,6 +145,10 @@ export interface ConfigProviderProps {
popupMatchSelectWidth?: boolean;
popupOverflow?: PopupOverflow;
theme?: ThemeConfig;
// TODO: wait for https://github.com/ant-design/ant-design/discussions/44551
// warning?: WarningContextProps;
alert?: ComponentStyleConfig;
anchor?: ComponentStyleConfig;
button?: ButtonConfig;
@ -329,17 +337,9 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
colorPicker,
datePicker,
wave,
// warning: warningConfig,
} = props;
// =================================== Warning ===================================
if (process.env.NODE_ENV !== 'production') {
warning(
dropdownMatchSelectWidth === undefined,
'ConfigProvider',
'`dropdownMatchSelectWidth` is deprecated. Please use `popupMatchSelectWidth` instead.',
);
}
// =================================== Context ===================================
const getPrefixCls = React.useCallback(
(suffixCls: string, customizePrefixCls?: string) => {
@ -357,10 +357,9 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
);
const iconPrefixCls = customIconPrefixCls || parentContext.iconPrefixCls || defaultIconPrefixCls;
const shouldWrapSSR = iconPrefixCls !== parentContext.iconPrefixCls;
const csp = customCsp || parentContext.csp;
const wrapSSR = useStyle(iconPrefixCls, csp);
useStyle(iconPrefixCls, csp);
const mergedTheme = useTheme(theme, parentContext.theme);
@ -428,6 +427,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
colorPicker,
datePicker,
wave,
// warning: warningConfig,
};
const config = {
@ -468,7 +468,12 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
[iconPrefixCls, csp],
);
let childNode = shouldWrapSSR ? wrapSSR(children as ReactElement) : children;
let childNode = (
<>
<PropWarning dropdownMatchSelectWidth={dropdownMatchSelectWidth} />
{children}
</>
);
const validateMessages = React.useMemo(
() =>
@ -484,7 +489,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
if (Object.keys(validateMessages).length > 0) {
childNode = (
<ValidateMessagesContext.Provider value={validateMessages}>
{children}
{childNode}
</ValidateMessagesContext.Provider>
);
}
@ -556,6 +561,13 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
);
}
// ================================== Warning ===================================
// if (memoedConfig.warning) {
// childNode = (
// <WarningContext.Provider value={memoedConfig.warning}>{childNode}</WarningContext.Provider>
// );
// }
// =================================== Render ===================================
if (componentDisabled !== undefined) {
childNode = (

View File

@ -1,3 +1,5 @@
import * as React from 'react';
import { forwardRef, useContext, useImperativeHandle } from 'react';
import CalendarOutlined from '@ant-design/icons/CalendarOutlined';
import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
@ -5,11 +7,10 @@ import SwapRightOutlined from '@ant-design/icons/SwapRightOutlined';
import classNames from 'classnames';
import { RangePicker as RCRangePicker } from 'rc-picker';
import type { GenerateConfig } from 'rc-picker/lib/generate/index';
import * as React from 'react';
import { forwardRef, useContext, useImperativeHandle } from 'react';
import type { RangePickerProps } from '.';
import { getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
import warning from '../../_util/warning';
import { devUseWarning } from '../../_util/warning';
import { ConfigContext } from '../../config-provider';
import DisabledContext from '../../config-provider/DisabledContext';
import useSize from '../../config-provider/hooks/useSize';
@ -77,9 +78,12 @@ export default function generateRangePicker<DateType>(generateConfig: GenerateCo
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!dropdownClassName,
'DatePicker.RangePicker',
'deprecated',
'`dropdownClassName` is deprecated. Please use `popupClassName` instead.',
);
}

View File

@ -1,3 +1,5 @@
import * as React from 'react';
import { forwardRef, useContext, useImperativeHandle } from 'react';
import CalendarOutlined from '@ant-design/icons/CalendarOutlined';
import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
@ -5,12 +7,11 @@ import classNames from 'classnames';
import RCPicker from 'rc-picker';
import type { GenerateConfig } from 'rc-picker/lib/generate/index';
import type { PickerMode } from 'rc-picker/lib/interface';
import * as React from 'react';
import { forwardRef, useContext, useImperativeHandle } from 'react';
import type { PickerProps, PickerTimeProps } from '.';
import type { InputStatus } from '../../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
import warning from '../../_util/warning';
import { devUseWarning } from '../../_util/warning';
import { ConfigContext } from '../../config-provider';
import DisabledContext from '../../config-provider/DisabledContext';
import useSize from '../../config-provider/hooks/useSize';
@ -105,15 +106,19 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
picker !== 'quarter',
displayName!,
'deprecated',
`DatePicker.${displayName} is legacy usage. Please use DatePicker[picker='${picker}'] directly.`,
);
warning(
!dropdownClassName,
displayName || 'DatePicker',
'deprecated',
'`dropdownClassName` is deprecated. Please use `popupClassName` instead.',
);
}

View File

@ -1,34 +1,36 @@
import { useMemo } from 'react';
import type { InternalDescriptionsItemType } from '..';
import warning from '../../_util/warning';
import { devUseWarning } from '../../_util/warning';
function getFilledItem(
rowItem: InternalDescriptionsItemType,
rowRestCol: number,
span?: number,
): InternalDescriptionsItemType {
): [item: InternalDescriptionsItemType, exceed: boolean] {
let clone = rowItem;
let exceed = false;
if (span === undefined || span > rowRestCol) {
clone = {
...rowItem,
span: rowRestCol,
};
warning(
span === undefined,
'Descriptions',
'Sum of column `span` in a line not match `column` of Descriptions.',
);
exceed = span !== undefined;
}
return clone;
return [clone, exceed];
}
// Calculate the sum of span in a row
function getCalcRows(rowItems: InternalDescriptionsItemType[], mergedColumn: number) {
function getCalcRows(
rowItems: InternalDescriptionsItemType[],
mergedColumn: number,
): [rows: InternalDescriptionsItemType[][], exceed: boolean] {
const rows: InternalDescriptionsItemType[][] = [];
let tmpRow: InternalDescriptionsItemType[] = [];
let rowRestCol = mergedColumn;
let exceed = false;
rowItems
.filter((n) => n)
@ -38,7 +40,10 @@ function getCalcRows(rowItems: InternalDescriptionsItemType[], mergedColumn: num
// Additional handle last one
if (index === rowItems.length - 1) {
tmpRow.push(getFilledItem(rowItem, rowRestCol, span));
const [item, itemExceed] = getFilledItem(rowItem, rowRestCol, span);
exceed = exceed || itemExceed;
tmpRow.push(item);
rows.push(tmpRow);
return;
}
@ -47,18 +52,32 @@ function getCalcRows(rowItems: InternalDescriptionsItemType[], mergedColumn: num
rowRestCol -= mergedSpan;
tmpRow.push(rowItem);
} else {
tmpRow.push(getFilledItem(rowItem, rowRestCol, mergedSpan));
const [item, itemExceed] = getFilledItem(rowItem, rowRestCol, mergedSpan);
exceed = exceed || itemExceed;
tmpRow.push(item);
rows.push(tmpRow);
rowRestCol = mergedColumn;
tmpRow = [];
}
});
return rows;
return [rows, exceed];
}
const useRow = (mergedColumn: number, items: InternalDescriptionsItemType[]) => {
const rows = useMemo(() => getCalcRows(items, mergedColumn), [items, mergedColumn]);
const [rows, exceed] = useMemo(() => getCalcRows(items, mergedColumn), [items, mergedColumn]);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!exceed,
'Descriptions',
'usage',
'Sum of column `span` in a line not match `column` of Descriptions.',
);
}
return rows;
};

View File

@ -1,8 +1,8 @@
import classNames from 'classnames';
import * as React from 'react';
import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import classNames from 'classnames';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import useStyle from './style';
export interface DividerProps {
@ -76,9 +76,12 @@ const Divider: React.FC<DividerProps> = (props) => {
// Warning children not work in vertical mode
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!children || type !== 'vertical',
'Divider',
'usage',
'`children` not working in `vertical` mode.',
);
}

View File

@ -1,20 +1,20 @@
import * as React from 'react';
import classNames from 'classnames';
import type { DrawerProps as RcDrawerProps } from 'rc-drawer';
import RcDrawer from 'rc-drawer';
import type { Placement } from 'rc-drawer/lib/Drawer';
import type { CSSMotionProps } from 'rc-motion';
import * as React from 'react';
import { getTransitionName } from '../_util/motion';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import { NoFormStyle } from '../form/context';
import type { DrawerPanelProps } from './DrawerPanel';
import DrawerPanel from './DrawerPanel';
// CSSINJS
import { NoCompactStyle } from '../space/Compact';
import useStyle from './style';
import { usePanelRef } from '../watermark/context';
import type { DrawerPanelProps } from './DrawerPanel';
import DrawerPanel from './DrawerPanel';
import useStyle from './style';
const SizeTypes = ['default', 'large'] as const;
type sizeType = typeof SizeTypes[number];
@ -88,6 +88,8 @@ const Drawer: React.FC<DrawerProps> & {
// ========================== Warning ===========================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
[
['visible', 'open'],
['afterVisibleChange', 'afterOpenChange'],
@ -95,6 +97,7 @@ const Drawer: React.FC<DrawerProps> & {
warning(
!(deprecatedName in props),
'Drawer',
'deprecated',
`\`${deprecatedName}\` is deprecated, please use \`${newName}\` instead.`,
);
});
@ -103,6 +106,7 @@ const Drawer: React.FC<DrawerProps> & {
warning(
false,
'Drawer',
'breaking',
'`style` is replaced by `rootStyle` in v5. Please check that `position: absolute` is necessary.',
);
}

View File

@ -11,7 +11,7 @@ import type { AdjustOverflow } from '../_util/placements';
import getPlacements from '../_util/placements';
import genPurePanel from '../_util/PurePanel';
import { cloneElement } from '../_util/reactNode';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { MenuProps } from '../menu';
import Menu from '../menu';
@ -109,6 +109,8 @@ const Dropdown: CompoundedComponent = (props) => {
} = React.useContext(ConfigContext);
// Warning for deprecated usage
const warning = devUseWarning();
if (process.env.NODE_ENV !== 'production') {
[
['visible', 'open'],
@ -117,6 +119,7 @@ const Dropdown: CompoundedComponent = (props) => {
warning(
!(deprecatedName in props),
'Dropdown',
'deprecated',
`\`${deprecatedName}\` is deprecated which will be removed in next major version, please use \`${newName}\` instead.`,
);
});
@ -124,6 +127,7 @@ const Dropdown: CompoundedComponent = (props) => {
warning(
!('overlay' in props),
'Dropdown',
'deprecated',
'`overlay` is deprecated. Please use `menu` instead.',
);
}
@ -146,19 +150,23 @@ const Dropdown: CompoundedComponent = (props) => {
}
if (placement.includes('Center')) {
const newPlacement = placement.slice(0, placement.indexOf('Center')) as DropdownPlacement;
warning(
!placement.includes('Center'),
'Dropdown',
`You are using '${placement}' placement in Dropdown, which is deprecated. Try to use '${newPlacement}' instead.`,
);
return newPlacement;
return placement.slice(0, placement.indexOf('Center')) as DropdownPlacement;
}
return placement as DropdownPlacement;
}, [placement, direction]);
if (process.env.NODE_ENV !== 'production') {
if (placement.includes('Center')) {
const newPlacement = placement.slice(0, placement.indexOf('Center')) as DropdownPlacement;
warning(
!placement.includes('Center'),
'Dropdown',
'deprecated',
`You are using '${placement}' placement in Dropdown, which is deprecated. Try to use '${newPlacement}' instead.`,
);
}
[
['visible', 'open'],
['onVisibleChange', 'onOpenChange'],
@ -166,6 +174,7 @@ const Dropdown: CompoundedComponent = (props) => {
warning(
!(deprecatedName in props),
'Dropdown',
'deprecated',
`\`${deprecatedName}\` is deprecated, please use \`${newName}\` instead.`,
);
});
@ -258,6 +267,7 @@ const Dropdown: CompoundedComponent = (props) => {
warning(
!mode || mode === 'vertical',
'Dropdown',
'usage',
`mode="${mode}" is not supported for Dropdown's Menu.`,
);
}}

View File

@ -1,13 +1,14 @@
import React, { useContext, useMemo } from 'react';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import React, { useContext, useMemo } from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import Badge from '../badge';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import Tooltip from '../tooltip';
import Content from './FloatButtonContent';
import FloatButtonGroupContext from './context';
import Content from './FloatButtonContent';
import type {
CompoundedComponent,
FloatButtonBadgeProps,
@ -84,9 +85,12 @@ const FloatButton: React.ForwardRefRenderFunction<
}
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!(shape === 'circle' && description),
'FloatButton',
'usage',
'supported only when `shape` is `square`. Due to narrow space for text, short sentence is recommended.',
);
}

View File

@ -1,14 +1,15 @@
import React, { memo, useCallback, useContext, useEffect, useMemo, useRef } from 'react';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import FileTextOutlined from '@ant-design/icons/FileTextOutlined';
import classNames from 'classnames';
import CSSMotion from 'rc-motion';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import React, { memo, useCallback, useContext, useEffect, useMemo, useRef } from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import FloatButton, { floatButtonPrefixCls } from './FloatButton';
import { FloatButtonGroupProvider } from './context';
import FloatButton, { floatButtonPrefixCls } from './FloatButton';
import type { FloatButtonGroupProps } from './interface';
import useStyle from './style';
@ -93,9 +94,12 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!('open' in props) || !!trigger,
'FloatButton.Group',
'usage',
'`open` need to be used together with `trigger`',
);
}

View File

@ -2,12 +2,12 @@ import * as React from 'react';
import classNames from 'classnames';
import { Field, FieldContext, ListContext } from 'rc-field-form';
import type { FieldProps } from 'rc-field-form/lib/Field';
import type { Meta, NamePath } from 'rc-field-form/lib/interface';
import type { Meta } from 'rc-field-form/lib/interface';
import useState from 'rc-util/lib/hooks/useState';
import { supportRef } from 'rc-util/lib/ref';
import { cloneElement, isValidElement } from '../../_util/reactNode';
import warning from '../../_util/warning';
import { devUseWarning } from '../../_util/warning';
import { ConfigContext } from '../../config-provider';
import { FormContext, NoStyleItemContext } from '../context';
import type { FormInstance } from '../Form';
@ -80,13 +80,6 @@ export interface FormItemProps<Values = any>
fieldKey?: React.Key | React.Key[];
}
function hasValidName(name?: NamePath): Boolean {
if (name === null) {
warning(false, 'Form.Item', '`null` is passed as `name` property');
}
return !(name === undefined || name === null);
}
function genEmptyMeta(): Meta {
return {
errors: [],
@ -128,13 +121,20 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
const mergedValidateTrigger =
validateTrigger !== undefined ? validateTrigger : contextValidateTrigger;
const hasName = hasValidName(name);
const hasName = !(name === undefined || name === null);
const prefixCls = getPrefixCls('form', customizePrefixCls);
// Style
const [wrapSSR, hashId] = useStyle(prefixCls);
// ========================= Warn =========================
const warning = devUseWarning();
if (process.env.NODE_ENV !== 'production') {
warning(name !== null, 'Form.Item', 'usage', '`null` is passed as `name` property');
}
// ========================= MISC =========================
// Get `noStyle` required info
const listContext = React.useContext(ListContext);
@ -308,12 +308,14 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
warning(
!(shouldUpdate && dependencies),
'Form.Item',
'usage',
"`shouldUpdate` and `dependencies` shouldn't be used together. See https://u.ant.design/form-deps.",
);
if (Array.isArray(mergedChildren) && hasName) {
warning(
false,
'Form.Item',
'usage',
'A `Form.Item` with a `name` prop must have a single child element. For information on how to render more complex form items, see https://u.ant.design/complex-form-item.',
);
childNode = mergedChildren;
@ -321,23 +323,27 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
warning(
!!(shouldUpdate || dependencies),
'Form.Item',
'usage',
'A `Form.Item` with a render function must have either `shouldUpdate` or `dependencies`.',
);
warning(
!hasName,
'Form.Item',
'usage',
'A `Form.Item` with a render function cannot be a field, and thus cannot have a `name` prop.',
);
} else if (dependencies && !isRenderProps && !hasName) {
warning(
false,
'Form.Item',
'usage',
'Must set `name` or use a render function when `dependencies` is set.',
);
} else if (isValidElement(mergedChildren)) {
warning(
mergedChildren.props.defaultValue === undefined,
'Form.Item',
'usage',
'`defaultValue` will not work on controlled Field. You should use `initialValues` of Form instead.',
);
@ -404,6 +410,7 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
warning(
!mergedName.length,
'Form.Item',
'usage',
'`name` is only used for validate React element. If you are using Form.Item as layout display, please remove `name` instead.',
);
childNode = mergedChildren as React.ReactNode;

View File

@ -1,7 +1,8 @@
import * as React from 'react';
import { List } from 'rc-field-form';
import type { StoreValue, ValidatorRule } from 'rc-field-form/lib/interface';
import * as React from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import { FormItemPrefixContext } from './context';
@ -35,12 +36,17 @@ const FormList: React.FC<FormListProps> = ({
children,
...props
}) => {
warning(
typeof props.name === 'number' ||
(Array.isArray(props.name) ? !!props.name.length : !!props.name),
'Form.List',
'Miss `name` prop.',
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
typeof props.name === 'number' ||
(Array.isArray(props.name) ? !!props.name.length : !!props.name),
'Form.List',
'usage',
'Miss `name` prop.',
);
}
const { getPrefixCls } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('form', customizePrefixCls);

View File

@ -1,7 +1,8 @@
import { useContext } from 'react';
import type { ValidateStatus } from 'antd/es/form/FormItem';
import { devUseWarning } from '../../_util/warning';
import { FormItemInputContext } from '../context';
import warning from '../../_util/warning';
type UseFormItemStatus = () => {
status?: ValidateStatus;
@ -12,11 +13,16 @@ type UseFormItemStatus = () => {
const useFormItemStatus: UseFormItemStatus = () => {
const { status, errors = [], warnings = [] } = useContext(FormItemInputContext);
warning(
status !== undefined,
'Form.Item',
'Form.Item.useStatus should be used under Form.Item component. For more information: https://u.ant.design/form-item-usestatus',
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
status !== undefined,
'Form.Item',
'usage',
'Form.Item.useStatus should be used under Form.Item component. For more information: https://u.ant.design/form-item-usestatus',
);
}
return { status, errors, warnings };
};

View File

@ -1,15 +1,18 @@
import { useEffect } from 'react';
import warning from '../../_util/warning';
import { devUseWarning } from '../../_util/warning';
import type { FormProps } from '../Form';
const names: Record<string, number> = {};
export default function useFormWarning({ name }: FormProps) {
const warning = devUseWarning();
useEffect(() => {
if (name) {
names[name] = (names[name] || 0) + 1;
warning(names[name] <= 1, 'Form', 'There exist multiple Form with same `name`.');
warning(names[name] <= 1, 'Form', 'usage', 'There exist multiple Form with same `name`.');
return () => {
names[name] -= 1;

View File

@ -1,8 +1,10 @@
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
const Icon: React.FC = () => {
if (process.env.NODE_ENV !== 'production') {
warning(false, 'Icon', 'Empty Icon');
const warning = devUseWarning();
warning(false, 'Icon', 'usage', 'Empty Icon');
}
return null;
};

View File

@ -1,7 +1,8 @@
import classNames from 'classnames';
import * as React from 'react';
import { useContext, useMemo } from 'react';
import warning from '../_util/warning';
import classNames from 'classnames';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { FormItemStatusContextProps } from '../form/context';
import { FormItemInputContext } from '../form/context';
@ -49,9 +50,12 @@ const Group: React.FC<GroupProps> = (props) => {
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
false,
'Input.Group',
'deprecated',
`'Input.Group' is deprecated. Please use 'Space.Compact' instead.`,
);
}

View File

@ -1,17 +1,18 @@
import React, { forwardRef, useContext, useEffect, useRef } from 'react';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import classNames from 'classnames';
import type { InputRef, InputProps as RcInputProps } from 'rc-input';
import RcInput from 'rc-input';
import type { BaseInputProps } from 'rc-input/lib/interface';
import { composeRef } from 'rc-util/lib/ref';
import React, { forwardRef, useContext, useEffect, useRef } from 'react';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import type { SizeType } from '../config-provider/SizeContext';
import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext, NoFormStyle } from '../form/context';
import { NoCompactStyle, useCompactItemContext } from '../space/Compact';
import useRemovePasswordTimeout from './hooks/useRemovePasswordTimeout';
@ -112,16 +113,24 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
// ===================== Focus warning =====================
const inputHasPrefixSuffix = hasPrefixSuffix(props) || !!hasFeedback;
const prevHasPrefixSuffix = useRef<boolean>(inputHasPrefixSuffix);
useEffect(() => {
if (inputHasPrefixSuffix && !prevHasPrefixSuffix.current) {
warning(
document.activeElement === inputRef.current?.input,
'Input',
`When Input is focused, dynamic add or remove prefix / suffix will make it lose focus caused by dom structure change. Read more: https://ant.design/components/input/#FAQ`,
);
}
prevHasPrefixSuffix.current = inputHasPrefixSuffix;
}, [inputHasPrefixSuffix]);
/* eslint-disable react-hooks/rules-of-hooks */
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
useEffect(() => {
if (inputHasPrefixSuffix && !prevHasPrefixSuffix.current) {
warning(
document.activeElement === inputRef.current?.input,
'Input',
'usage',
`When Input is focused, dynamic add or remove prefix / suffix will make it lose focus caused by dom structure change. Read more: https://ant.design/components/input/#FAQ`,
);
}
prevHasPrefixSuffix.current = inputHasPrefixSuffix;
}, [inputHasPrefixSuffix]);
}
/* eslint-enable */
// ===================== Remove Password value =====================
const removePasswordTimeout = useRemovePasswordTimeout(inputRef, true);

View File

@ -1,6 +1,7 @@
import type { ValidateMessages } from 'rc-field-form/lib/interface';
import * as React from 'react';
import warning from '../_util/warning';
import type { ValidateMessages } from 'rc-field-form/lib/interface';
import { devUseWarning } from '../_util/warning';
import type { PickerLocale as DatePickerLocale } from '../date-picker/generatePicker';
import type { TransferLocale as TransferLocaleForEmpty } from '../empty';
import type { ModalLocale } from '../modal/locale';
@ -68,9 +69,12 @@ const LocaleProvider: React.FC<LocaleProviderProps> = (props) => {
const { locale = {} as Locale, children, _ANT_MARK__ } = props;
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
_ANT_MARK__ === ANT_MARK,
'LocaleProvider',
'deprecated',
'`LocaleProvider` is deprecated. Please use `locale` with `ConfigProvider` instead: http://u.ant.design/locale',
);
}

View File

@ -1,3 +1,5 @@
// eslint-disable-next-line import/no-named-as-default
import * as React from 'react';
import classNames from 'classnames';
import RcMentions from 'rc-mentions';
import type {
@ -6,17 +8,15 @@ import type {
MentionsRef as RcMentionsRef,
} from 'rc-mentions/lib/Mentions';
import { composeRef } from 'rc-util/lib/ref';
// eslint-disable-next-line import/no-named-as-default
import * as React from 'react';
import genPurePanel from '../_util/PurePanel';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import { FormItemInputContext } from '../form/context';
import Spin from '../spin';
import useStyle from './style';
export const { Option } = RcMentions;
@ -88,9 +88,12 @@ const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!children,
'Mentions',
'deprecated',
'`Mentions.Option` is deprecated. Please use `options` instead.',
);
}

View File

@ -1,21 +1,22 @@
import * as React from 'react';
import { forwardRef } from 'react';
import EllipsisOutlined from '@ant-design/icons/EllipsisOutlined';
import classNames from 'classnames';
import type { MenuProps as RcMenuProps, MenuRef as RcMenuRef } from 'rc-menu';
import RcMenu from 'rc-menu';
import { useEvent } from 'rc-util';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import { forwardRef } from 'react';
import initCollapseMotion from '../_util/motion';
import { cloneElement, isValidElement } from '../_util/reactNode';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { SiderContextProps } from '../layout/Sider';
import type { ItemType } from './hooks/useItems';
import useItems from './hooks/useItems';
import type { MenuContextProps, MenuTheme } from './MenuContext';
import MenuContext from './MenuContext';
import OverrideContext from './OverrideContext';
import type { ItemType } from './hooks/useItems';
import useItems from './hooks/useItems';
import useStyle from './style';
export interface MenuProps extends Omit<RcMenuProps, 'items'> {
@ -70,23 +71,30 @@ const InternalMenu = forwardRef<RcMenuRef, InternalMenuProps>((props, ref) => {
const mergedChildren = useItems(items) || children;
// ======================== Warning ==========================
warning(
!('inlineCollapsed' in props && mode !== 'inline'),
'Menu',
'`inlineCollapsed` should only be used when `mode` is inline.',
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!(props.siderCollapsed !== undefined && 'inlineCollapsed' in props),
'Menu',
'`inlineCollapsed` not control Menu under Sider. Should set `collapsed` on Sider instead.',
);
warning(
!('inlineCollapsed' in props && mode !== 'inline'),
'Menu',
'usage',
'`inlineCollapsed` should only be used when `mode` is inline.',
);
warning(
'items' in props && !children,
'Menu',
'`children` will be removed in next major version. Please use `items` instead.',
);
warning(
!(props.siderCollapsed !== undefined && 'inlineCollapsed' in props),
'Menu',
'usage',
'`inlineCollapsed` not control Menu under Sider. Should set `collapsed` on Sider instead.',
);
warning(
'items' in props && !children,
'Menu',
'deprecated',
'`children` will be removed in next major version. Please use `items` instead.',
);
}
overrideObj.validator?.({ mode });

View File

@ -1,12 +1,14 @@
import * as React from 'react';
import type { FC, PropsWithChildren } from 'react';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import classNames from 'classnames';
import { NotificationProvider, useNotification as useRcNotification } from 'rc-notification';
import type { NotificationAPI } from 'rc-notification/lib';
import * as React from 'react';
import warning from '../_util/warning';
import type { NotificationConfig as RcNotificationConfig } from 'rc-notification/lib/useNotification';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { ComponentStyleConfig } from '../config-provider/context';
import { PureContent } from './PurePanel';
import type {
ArgsProps,
ConfigOptions,
@ -15,10 +17,9 @@ import type {
NoticeType,
TypeOpen,
} from './interface';
import { PureContent } from './PurePanel';
import useStyle from './style';
import { getMotion, wrapPromiseFn } from './util';
import type { FC, PropsWithChildren } from 'react';
import type { NotificationConfig as RcNotificationConfig } from 'rc-notification/lib/useNotification';
const DEFAULT_OFFSET = 8;
const DEFAULT_DURATION = 3;
@ -122,6 +123,8 @@ export function useInternalMessage(
): readonly [MessageInstance, React.ReactElement] {
const holderRef = React.useRef<HolderRef>(null);
const warning = devUseWarning();
// ================================ API ================================
const wrapAPI = React.useMemo<MessageInstance>(() => {
// Wrap with notification content
@ -137,6 +140,7 @@ export function useInternalMessage(
warning(
false,
'Message',
'usage',
'You are calling notice in render which will break in React 18 concurrent mode. Please trigger in effect instead.',
);

View File

@ -6,7 +6,7 @@ import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled';
import classNames from 'classnames';
import { getTransitionName } from '../_util/motion';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import type { ThemeConfig } from '../config-provider';
import ConfigProvider from '../config-provider';
import { useLocale } from '../locale';
@ -61,11 +61,16 @@ export function ConfirmContent(
...resetProps
} = props;
warning(
!(typeof icon === 'string' && icon.length > 2),
'Modal',
`\`icon\` is using ReactNode instead of string naming in v4. Please check \`${icon}\` at https://ant.design/components/icon`,
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!(typeof icon === 'string' && icon.length > 2),
'Modal',
'breaking',
`\`icon\` is using ReactNode instead of string naming in v4. Please check \`${icon}\` at https://ant.design/components/icon`,
);
}
// Icon
let mergedIcon: React.ReactNode = icon;
@ -185,9 +190,12 @@ const ConfirmDialog: React.FC<ConfirmDialogProps> = (props) => {
} = props;
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
visible === undefined,
'Modal',
'deprecated',
`\`visible\` is deprecated, please use \`open\` instead.`,
);
}

View File

@ -6,7 +6,7 @@ import Dialog from 'rc-dialog';
import useClosable from '../_util/hooks/useClosable';
import { getTransitionName } from '../_util/motion';
import { canUseDocElement } from '../_util/styleChecker';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import { NoFormStyle } from '../form/context';
import { NoCompactStyle } from '../space/Compact';
@ -54,11 +54,16 @@ const Modal: React.FC<ModalProps> = (props) => {
onOk?.(e);
};
warning(
!('visible' in props),
'Modal',
`\`visible\` will be removed in next major version, please use \`open\` instead.`,
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!('visible' in props),
'Modal',
'deprecated',
`\`visible\` will be removed in next major version, please use \`open\` instead.`,
);
}
const {
prefixCls: customizePrefixCls,
@ -90,10 +95,6 @@ const Modal: React.FC<ModalProps> = (props) => {
[`${prefixCls}-wrap-rtl`]: direction === 'rtl',
});
if (process.env.NODE_ENV !== 'production') {
warning(!('visible' in props), 'Modal', '`visible` is deprecated, please use `open` instead.');
}
const dialogFooter = footer !== null && (
<Footer {...props} onOk={handleOk} onCancel={handleCancel} />
);

View File

@ -117,7 +117,7 @@ describe('Modal', () => {
render(<Modal visible />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Modal] `visible` is deprecated, please use `open` instead.',
'Warning: [antd: Modal] `visible` will be removed in next major version, please use `open` instead.',
);
expect(document.querySelector('.ant-modal')).toBeTruthy();

View File

@ -1,11 +1,13 @@
import * as React from 'react';
import type { FC, PropsWithChildren } from 'react';
import classNames from 'classnames';
import { NotificationProvider, useNotification as useRcNotification } from 'rc-notification';
import type {
NotificationAPI,
NotificationConfig as RcNotificationConfig,
} from 'rc-notification/lib';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { ComponentStyleConfig } from '../config-provider/context';
import type {
@ -17,7 +19,6 @@ import type {
import { getCloseIcon, PureContent } from './PurePanel';
import useStyle from './style';
import { getMotion, getPlacementStyle } from './util';
import type { FC, PropsWithChildren } from 'react';
const DEFAULT_OFFSET = 24;
const DEFAULT_DURATION = 4.5;
@ -109,6 +110,8 @@ export function useInternalNotification(
): readonly [NotificationInstance, React.ReactElement] {
const holderRef = React.useRef<HolderRef>(null);
const warning = devUseWarning();
// ================================ API ================================
const wrapAPI = React.useMemo<NotificationInstance>(() => {
// Wrap with notification content
@ -119,6 +122,7 @@ export function useInternalNotification(
warning(
false,
'Notification',
'usage',
'You are calling notice in render which will break in React 18 concurrent mode. Please trigger in effect instead.',
);
return;

View File

@ -1,7 +1,8 @@
import { presetPrimaryColors } from '@ant-design/colors';
import * as React from 'react';
import { presetPrimaryColors } from '@ant-design/colors';
import { devUseWarning } from '../_util/warning';
import type { DirectionType } from '../config-provider';
import warning from '../_util/warning';
import type { ProgressGradient, ProgressProps, StringGradients } from './progress';
import { getSize, getSuccessPercent, validProgress } from './utils';
@ -98,9 +99,12 @@ const Line: React.FC<LineProps> = (props) => {
const [width, height] = getSize(mergedSize, 'line', { strokeWidth });
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!('strokeWidth' in props),
'Progress',
'deprecated',
'`strokeWidth` is deprecated. Please use `size` instead.',
);
}

View File

@ -260,7 +260,7 @@ describe('Progress', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<Progress size={[60, 20]} type="circle" />);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Progress] Type "circle" and "dashbord" do not accept array as `size`, please use number or preset size instead.',
'Warning: [antd: Progress] Type "circle" and "dashboard" do not accept array as `size`, please use number or preset size instead.',
);
});
@ -275,7 +275,7 @@ describe('Progress', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<Progress size={[60, 20]} type="dashboard" />);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Progress] Type "circle" and "dashbord" do not accept array as `size`, please use number or preset size instead.',
'Warning: [antd: Progress] Type "circle" and "dashboard" do not accept array as `size`, please use number or preset size instead.',
);
});

View File

@ -6,7 +6,7 @@ import CloseOutlined from '@ant-design/icons/CloseOutlined';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import Circle from './Circle';
@ -123,12 +123,38 @@ const Progress = React.forwardRef<HTMLDivElement, ProgressProps>((props, ref) =>
}, [showInfo, percent, percentNumber, progressStatus, type, prefixCls, format]);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!('successPercent' in props),
'Progress',
'deprecated',
'`successPercent` is deprecated. Please use `success.percent` instead.',
);
warning(!('width' in props), 'Progress', '`width` is deprecated. Please use `size` instead.');
warning(
!('width' in props),
'Progress',
'deprecated',
'`width` is deprecated. Please use `size` instead.',
);
if ((type === 'circle' || type === 'dashboard') && Array.isArray(size)) {
warning(
false,
'Progress',
'usage',
'Type "circle" and "dashboard" do not accept array as `size`, please use number or preset size instead.',
);
}
if (props.success && 'progress' in props.success) {
warning(
false,
'Progress',
'deprecated',
'`success.progress` is deprecated. Please use `success.percent` instead.',
);
}
}
const strokeColorNotArray = Array.isArray(strokeColor) ? strokeColor[0] : strokeColor;

View File

@ -1,5 +1,5 @@
import { presetPrimaryColors } from '@ant-design/colors';
import warning from '../_util/warning';
import type { CircleProps } from './Circle';
import type { ProgressProps } from './progress';
@ -17,11 +17,6 @@ export function getSuccessPercent({ success, successPercent }: ProgressProps) {
let percent = successPercent;
/** @deprecated Use `percent` instead */
if (success && 'progress' in success) {
warning(
false,
'Progress',
'`success.progress` is deprecated. Please use `success.percent` instead.',
);
percent = success.progress;
}
if (success && 'percent' in success) {
@ -80,14 +75,6 @@ export const getSize = (
} else if (typeof size === 'number') {
[width, height] = [size, size];
} else {
if (process.env.NODE_ENV !== 'production') {
warning(
false,
'Progress',
'Type "circle" and "dashbord" do not accept array as `size`, please use number or preset size instead.',
);
}
width = (size[0] ?? size[1] ?? 120) as number;
height = (size[0] ?? size[1] ?? 120) as number;
}

View File

@ -3,7 +3,7 @@ import ReloadOutlined from '@ant-design/icons/ReloadOutlined';
import classNames from 'classnames';
import { QRCodeCanvas, QRCodeSVG } from 'qrcode.react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import Button from '../button';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
@ -56,21 +56,23 @@ const QRCode: React.FC<QRCodeProps> = (props) => {
const [locale] = useLocale('QRCode');
if (!value) {
if (process.env.NODE_ENV !== 'production') {
warning(false, 'QRCode', 'need to receive `value` props');
}
return null;
}
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(!!value, 'QRCode', 'usage', 'need to receive `value` props');
warning(
!(icon && errorLevel === 'L'),
'QRCode',
'usage',
'ErrorLevel `L` is not recommended to be used with `icon`, for scanning result would be affected by low level.',
);
}
if (!value) {
return null;
}
const cls = classNames(prefixCls, className, rootClassName, hashId, {
[`${prefixCls}-borderless`]: !bordered,
});

View File

@ -1,18 +1,18 @@
import * as React from 'react';
import classNames from 'classnames';
import type { CheckboxRef } from 'rc-checkbox';
import RcCheckbox from 'rc-checkbox';
import { composeRef } from 'rc-util/lib/ref';
import * as React from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import Wave from '../_util/wave';
import { TARGET_CLS } from '../_util/wave/interface';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import { FormItemInputContext } from '../form/context';
import RadioGroupContext, { RadioOptionTypeContext } from './context';
import type { RadioChangeEvent, RadioProps } from './interface';
import useStyle from './style';
import Wave from '../_util/wave';
import { TARGET_CLS } from '../_util/wave/interface';
const InternalRadio: React.ForwardRefRenderFunction<CheckboxRef, RadioProps> = (props, ref) => {
const groupContext = React.useContext(RadioGroupContext);
@ -23,7 +23,16 @@ const InternalRadio: React.ForwardRefRenderFunction<CheckboxRef, RadioProps> = (
const mergedRef = composeRef(ref, innerRef);
const { isFormItemInput } = React.useContext(FormItemInputContext);
warning(!('optionType' in props), 'Radio', '`optionType` is only support in Radio.Group.');
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!('optionType' in props),
'Radio',
'usage',
'`optionType` is only support in Radio.Group.',
);
}
const onChange = (e: RadioChangeEvent) => {
props.onChange?.(e);

View File

@ -1,18 +1,16 @@
import * as React from 'react';
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import WarningFilled from '@ant-design/icons/WarningFilled';
import classNames from 'classnames';
import * as React from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import noFound from './noFound';
import serverError from './serverError';
import unauthorized from './unauthorized';
import useStyle from './style';
import unauthorized from './unauthorized';
export const IconMap = {
success: CheckCircleFilled,
@ -62,11 +60,16 @@ interface IconProps {
const Icon: React.FC<IconProps> = ({ prefixCls, icon, status }) => {
const className = classNames(`${prefixCls}-icon`);
warning(
!(typeof icon === 'string' && icon.length > 2),
'Result',
`\`icon\` is using ReactNode instead of string naming in v4. Please check \`${icon}\` at https://ant.design/components/icon`,
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!(typeof icon === 'string' && icon.length > 2),
'Result',
'breaking',
`\`icon\` is using ReactNode instead of string naming in v4. Please check \`${icon}\` at https://ant.design/components/icon`,
);
}
if (ExceptionStatus.includes(`${status}`)) {
const SVGComponent = ExceptionMap[status as ExceptionStatusType];

View File

@ -1,28 +1,29 @@
// TODO: 4.0 - codemod should help to change `filterOption` to support node props.
import * as React from 'react';
import classNames from 'classnames';
import type { BaseSelectRef, SelectProps as RcSelectProps } from 'rc-select';
import RcSelect, { OptGroup, Option } from 'rc-select';
import type { OptionProps } from 'rc-select/lib/Option';
import type { BaseOptionType, DefaultOptionType } from 'rc-select/lib/Select';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import genPurePanel from '../_util/PurePanel';
import type { SelectCommonPlacement } from '../_util/motion';
import { getTransitionName } from '../_util/motion';
import genPurePanel from '../_util/PurePanel';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import type { SizeType } from '../config-provider/SizeContext';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import DisabledContext from '../config-provider/DisabledContext';
import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context';
import { useCompactItemContext } from '../space/Compact';
import useStyle from './style';
import useBuiltinPlacements from './useBuiltinPlacements';
import useShowArrow from './useShowArrow';
import getIcons from './utils/iconUtil';
import useIcons from './useIcons';
type RawValue = string | number;
@ -161,7 +162,7 @@ const InternalSelect = <
}
// ===================== Icons =====================
const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons({
const { suffixIcon, itemIcon, removeIcon, clearIcon } = useIcons({
...props,
multiple: isMultiple,
hasFeedback,
@ -222,21 +223,26 @@ const InternalSelect = <
// ====================== Warning ======================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!dropdownClassName,
'Select',
'deprecated',
'`dropdownClassName` is deprecated. Please use `popupClassName` instead.',
);
warning(
dropdownMatchSelectWidth === undefined,
'Select',
'deprecated',
'`dropdownMatchSelectWidth` is deprecated. Please use `popupMatchSelectWidth` instead.',
);
warning(
!('showArrow' in props),
'Select',
'deprecated',
'`showArrow` is deprecated which will be removed in next major version. It will be a default behavior, you can hide it by setting `suffixIcon` to null.',
);
}

View File

@ -1,16 +1,17 @@
import type { ReactNode } from 'react';
import * as React from 'react';
import CheckOutlined from '@ant-design/icons/CheckOutlined';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import DownOutlined from '@ant-design/icons/DownOutlined';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import SearchOutlined from '@ant-design/icons/SearchOutlined';
import type { ReactNode } from 'react';
import * as React from 'react';
import warning from '../../_util/warning';
import { devUseWarning } from '../_util/warning';
type RenderNode = React.ReactNode | ((props: any) => React.ReactNode);
export default function getIcons({
export default function useIcons({
suffixIcon,
clearIcon,
menuItemSelectedIcon,
@ -38,9 +39,12 @@ export default function getIcons({
componentName: string;
}) {
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!clearIcon,
componentName,
'deprecated',
'`clearIcon` is deprecated, please use `allowClear={{ clearIcon: React.ReactNode }}` instead.',
);
}

View File

@ -1,9 +1,10 @@
import React from 'react';
import classNames from 'classnames';
import type { SliderProps as RcSliderProps } from 'rc-slider';
import RcSlider from 'rc-slider';
import type { SliderRef } from 'rc-slider/lib/Slider';
import React from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import type { TooltipPlacement } from '../tooltip';
@ -165,6 +166,8 @@ const Slider = React.forwardRef<SliderRef, SliderSingleProps | SliderRangeProps>
// Warning for deprecated usage
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
[
['tooltipPrefixCls', 'prefixCls'],
['getTooltipPopupContainer', 'getPopupContainer'],
@ -175,6 +178,7 @@ const Slider = React.forwardRef<SliderRef, SliderSingleProps | SliderRangeProps>
warning(
!(deprecatedName in props),
'Slider',
'deprecated',
`\`${deprecatedName}\` is deprecated, please use \`tooltip.${newName}\` instead.`,
);
});

View File

@ -1,9 +1,10 @@
import * as React from 'react';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import { debounce } from 'throttle-debounce';
import { cloneElement, isValidElement } from '../_util/reactNode';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import useStyle from './style/index';
@ -110,7 +111,9 @@ const Spin: React.FC<SpinClassProps> = (props) => {
const isNestedPattern = React.useMemo<boolean>(() => typeof children !== 'undefined', [children]);
if (process.env.NODE_ENV !== 'production') {
warning(!tip || isNestedPattern, 'Spin', '`tip` only work in nest pattern.');
const warning = devUseWarning();
warning(!tip || isNestedPattern, 'Spin', 'usage', '`tip` only work in nest pattern.');
}
const { direction, spin } = React.useContext<ConfigConsumerProps>(ConfigContext);

View File

@ -1,19 +1,23 @@
import * as React from 'react';
import toArray from 'rc-util/lib/Children/toArray';
import type { StepProps } from '.';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
function filter<T>(items: (T | null)[]): T[] {
return items.filter((item) => item) as T[];
}
export default function useLegacyItems(items?: StepProps[], children?: React.ReactNode) {
if (process.env.NODE_ENV === 'test') {
const warning = devUseWarning();
warning(!children, 'Steps', 'deprecated', 'Step is deprecated. Please use `items` directly.');
}
if (items) {
return items;
}
warning(!children, 'Steps', 'Step is deprecated. Please use `items` directly.');
const childrenItems = toArray(children).map((node: React.ReactElement<StepProps>) => {
if (React.isValidElement(node)) {
const { props } = node;

View File

@ -1,8 +1,9 @@
import * as React from 'react';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import classNames from 'classnames';
import RcSwitch from 'rc-switch';
import * as React from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import Wave from '../_util/wave';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
@ -55,11 +56,16 @@ const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>((props, ref) =>
...restProps
} = props;
warning(
'checked' in props || !('value' in props),
'Switch',
'`value` is not a valid prop, do you mean `checked`?',
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
'checked' in props || !('value' in props),
'Switch',
'usage',
'`value` is not a valid prop, do you mean `checked`?',
);
}
const { getPrefixCls, direction, switch: SWITCH } = React.useContext(ConfigContext);

View File

@ -7,7 +7,7 @@ import omit from 'rc-util/lib/omit';
import type { Breakpoint } from '../_util/responsiveObserver';
import scrollTo from '../_util/scrollTo';
import type { AnyObject } from '../_util/type';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import type { ConfigConsumerProps } from '../config-provider/context';
import { ConfigContext } from '../config-provider/context';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
@ -148,10 +148,13 @@ const InternalTable = <RecordType extends AnyObject = AnyObject>(
virtual,
} = props;
const warning = devUseWarning();
if (process.env.NODE_ENV !== 'production') {
warning(
!(typeof rowKey === 'function' && rowKey.length > 1),
'Table',
'usage',
'`index` parameter of `rowKey` function is deprecated. There is no guarantee that it will work as expected.',
);
}
@ -378,7 +381,7 @@ const InternalTable = <RecordType extends AnyObject = AnyObject>(
}
const { current = 1, total, pageSize = DEFAULT_PAGE_SIZE } = mergedPagination;
warning(current > 0, 'Table', '`current` should be positive number.');
warning(current > 0, 'Table', 'usage', '`current` should be positive number.');
// Dynamic table data
if (mergedData.length < total!) {
@ -386,6 +389,7 @@ const InternalTable = <RecordType extends AnyObject = AnyObject>(
warning(
false,
'Table',
'usage',
'`dataSource` length is less than `pagination.total` but large than `pagination.pageSize`. Please make sure your config correct data with async mode.',
);
return mergedData.slice((current - 1) * pageSize, current * pageSize);

View File

@ -1,11 +1,12 @@
import * as React from 'react';
import FilterFilled from '@ant-design/icons/FilterFilled';
import classNames from 'classnames';
import type { FieldDataNode } from 'rc-tree';
import isEqual from 'rc-util/lib/isEqual';
import * as React from 'react';
import type { FilterState } from '.';
import useSyncState from '../../../_util/hooks/useSyncState';
import warning from '../../../_util/warning';
import { devUseWarning } from '../../../_util/warning';
import Button from '../../../button';
import type { CheckboxChangeEvent } from '../../../checkbox';
import Checkbox from '../../../checkbox';
@ -173,6 +174,8 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
};
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
[
['filterDropdownVisible', 'filterDropdownOpen', filterDropdownVisible],
[
@ -184,6 +187,7 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
warning(
prop === undefined || prop === null,
'Table',
'deprecated',
`\`${deprecatedName}\` is deprecated. Please use \`${newName}\` instead.`,
);
});

View File

@ -1,5 +1,6 @@
import * as React from 'react';
import warning from '../../../_util/warning';
import { devUseWarning } from '../../../_util/warning';
import type {
ColumnsType,
ColumnTitleProps,
@ -203,6 +204,8 @@ function useFilter<RecordType>({
FilterState<RecordType>[],
Record<string, FilterValue | null>,
] {
const warning = devUseWarning();
const mergedColumns = React.useMemo(
() => getMergedColumns(rawMergedColumns || []),
[rawMergedColumns],
@ -251,6 +254,7 @@ function useFilter<RecordType>({
warning(
filteredKeysIsAllControlled,
'Table',
'usage',
'Columns should all contain `filteredValue` or not contain `filteredValue`.',
);

View File

@ -1,3 +1,5 @@
import * as React from 'react';
import { useCallback, useMemo, useState } from 'react';
import DownOutlined from '@ant-design/icons/DownOutlined';
import classNames from 'classnames';
import { INTERNAL_COL_DEFINE } from 'rc-table';
@ -7,17 +9,16 @@ import { arrAdd, arrDel } from 'rc-tree/lib/util';
import { conductCheck } from 'rc-tree/lib/utils/conductUtil';
import { convertDataToEntities } from 'rc-tree/lib/utils/treeUtil';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import * as React from 'react';
import { useCallback, useMemo, useState } from 'react';
import type { AnyObject } from '../../_util/type';
import warning from '../../_util/warning';
import { devUseWarning } from '../../_util/warning';
import type { CheckboxProps } from '../../checkbox';
import Checkbox from '../../checkbox';
import Dropdown from '../../dropdown';
import Radio from '../../radio';
import type {
ColumnType,
ColumnsType,
ColumnType,
ExpandType,
GetPopupContainer,
GetRowKey,
@ -106,6 +107,8 @@ const useSelection = <RecordType extends AnyObject = AnyObject>(
getPopupContainer,
} = config;
const warning = devUseWarning();
// ========================= Keys =========================
const [mergedSelectedKeys, setMergedSelectedKeys] = useMergedState(
selectedRowKeys || defaultSelectedRowKeys || EMPTY_LIST,
@ -180,6 +183,7 @@ const useSelection = <RecordType extends AnyObject = AnyObject>(
warning(
!('checked' in checkboxProps || 'defaultChecked' in checkboxProps),
'Table',
'usage',
'Do not set `checked` or `defaultChecked` in `getCheckboxProps`. Please use `selectedRowKeys` instead.',
);
});
@ -320,6 +324,7 @@ const useSelection = <RecordType extends AnyObject = AnyObject>(
warning(
false,
'Table',
'deprecated',
'`onSelectInvert` will be removed in future. Please use `onChange` instead.',
);
onSelectInvert(keys);
@ -364,6 +369,7 @@ const useSelection = <RecordType extends AnyObject = AnyObject>(
warning(
!columns.includes(SELECTION_COLUMN),
'Table',
'usage',
'`rowSelection` is not config but `SELECTION_COLUMN` exists in the `columns`.',
);
@ -517,6 +523,7 @@ const useSelection = <RecordType extends AnyObject = AnyObject>(
warning(
typeof checkboxProps?.indeterminate !== 'boolean',
'Table',
'usage',
'set `indeterminate` using `rowSelection.getCheckboxProps` is not allowed with tree structured dataSource.',
);
} else {
@ -664,6 +671,7 @@ const useSelection = <RecordType extends AnyObject = AnyObject>(
warning(
cloneColumns.filter((col) => col === SELECTION_COLUMN).length <= 1,
'Table',
'usage',
'Multiple `SELECTION_COLUMN` exist in `columns`.',
);

View File

@ -1,20 +1,29 @@
import * as React from 'react';
import toArray from 'rc-util/lib/Children/toArray';
import type { Tab } from 'rc-tabs/lib/interface';
import type { TabsProps, TabPaneProps } from '..';
import warning from '../../_util/warning';
import toArray from 'rc-util/lib/Children/toArray';
import type { TabPaneProps, TabsProps } from '..';
import { devUseWarning } from '../../_util/warning';
function filter<T>(items: (T | null)[]): T[] {
return items.filter((item) => item) as T[];
}
export default function useLegacyItems(items?: TabsProps['items'], children?: React.ReactNode) {
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!children,
'Tabs',
'deprecated',
'Tabs.TabPane is deprecated. Please use `items` directly.',
);
}
if (items) {
return items;
}
warning(!children, 'Tabs', 'Tabs.TabPane is deprecated. Please use `items` directly.');
const childrenItems = toArray(children).map((node: React.ReactElement<TabPaneProps>) => {
if (React.isValidElement(node)) {
const { key, props } = node;

View File

@ -1,3 +1,4 @@
import * as React from 'react';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import EllipsisOutlined from '@ant-design/icons/EllipsisOutlined';
import PlusOutlined from '@ant-design/icons/PlusOutlined';
@ -5,15 +6,15 @@ import classNames from 'classnames';
import type { TabsProps as RcTabsProps } from 'rc-tabs';
import RcTabs from 'rc-tabs';
import type { EditableConfig } from 'rc-tabs/lib/interface';
import * as React from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { SizeType } from '../config-provider/SizeContext';
import useSize from '../config-provider/hooks/useSize';
import TabPane, { type TabPaneProps } from './TabPane';
import type { SizeType } from '../config-provider/SizeContext';
import useAnimateConfig from './hooks/useAnimateConfig';
import useLegacyItems from './hooks/useLegacyItems';
import useStyle from './style';
import TabPane, { type TabPaneProps } from './TabPane';
export type TabsType = 'line' | 'card' | 'editable-card';
export type TabsPosition = 'top' | 'right' | 'bottom' | 'left';
@ -67,11 +68,16 @@ const Tabs: React.FC<TabsProps> & { TabPane: typeof TabPane } = (props) => {
}
const rootPrefixCls = getPrefixCls();
warning(
!('onPrevClick' in props) && !('onNextClick' in props),
'Tabs',
'`onPrevClick` and `onNextClick` has been removed. Please use `onTabScroll` instead.',
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!('onPrevClick' in props) && !('onNextClick' in props),
'Tabs',
'breaking',
'`onPrevClick` and `onNextClick` has been removed. Please use `onTabScroll` instead.',
);
}
const mergedItems = useLegacyItems(items, children);

View File

@ -6,7 +6,7 @@ import type { PresetColorType, PresetStatusColorType } from '../_util/colors';
import { isPresetColor, isPresetStatusColor } from '../_util/colors';
import useClosable from '../_util/hooks/useClosable';
import type { LiteralUnion } from '../_util/type';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import Wave from '../_util/wave';
import { ConfigContext } from '../config-provider';
import CheckableTag from './CheckableTag';
@ -57,9 +57,12 @@ const InternalTag: React.ForwardRefRenderFunction<HTMLSpanElement, TagProps> = (
// Warning for deprecated usage
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!('visible' in props),
'Tag',
'deprecated',
'`visible` is deprecated, please use `visible && <Tag />` instead.',
);
}

View File

@ -1,8 +1,9 @@
import type { Dayjs } from 'dayjs';
import * as React from 'react';
import type { Dayjs } from 'dayjs';
import genPurePanel from '../_util/PurePanel';
import type { InputStatus } from '../_util/statusUtils';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import DatePicker from '../date-picker';
import type { PickerTimeProps, RangePickerTimeProps } from '../date-picker/generatePicker';
@ -30,17 +31,23 @@ export interface TimePickerProps extends Omit<PickerTimeProps<Dayjs>, 'picker'>
const TimePicker = React.forwardRef<any, TimePickerProps>(
({ addon, renderExtraFooter, ...restProps }, ref) => {
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!addon,
'TimePicker',
'deprecated',
'`addon` is deprecated. Please use `renderExtraFooter` instead.',
);
}
const internalRenderExtraFooter = React.useMemo(() => {
if (renderExtraFooter) {
return renderExtraFooter;
}
if (addon) {
warning(
false,
'TimePicker',
'`addon` is deprecated. Please use `renderExtraFooter` instead.',
);
return addon;
}
return undefined;

View File

@ -1,15 +1,15 @@
import classNames from 'classnames';
import * as React from 'react';
import warning from '../_util/warning';
import classNames from 'classnames';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
// CSSINJS
import useStyle from './style';
import type { TimelineItemProps } from './TimelineItem';
import TimelineItem from './TimelineItem';
import TimelineItemList from './TimelineItemList';
import useItems from './useItems';
// CSSINJS
import useStyle from './style';
export interface TimelineProps {
prefixCls?: string;
className?: string;
@ -35,7 +35,14 @@ const Timeline: CompoundedComponent = (props) => {
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
warning(!children, 'Timeline', '`Timeline.Item` is deprecated. Please use `items` instead.');
const warning = devUseWarning();
warning(
!children,
'Timeline',
'deprecated',
'`Timeline.Item` is deprecated. Please use `items` instead.',
);
}
// Style

View File

@ -17,7 +17,7 @@ import type { AdjustOverflow, PlacementsConfig } from '../_util/placements';
import getPlacements from '../_util/placements';
import { cloneElement, isFragment, isValidElement } from '../_util/reactNode';
import type { LiteralUnion } from '../_util/type';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import { NoCompactStyle } from '../space/Compact';
import { useToken } from '../theme/internal';
@ -216,6 +216,8 @@ const Tooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
} = React.useContext(ConfigContext);
// ============================== Ref ===============================
const warning = devUseWarning();
const tooltipRef = React.useRef<RcTooltipRef>(null);
const forceAlign = () => {
@ -225,7 +227,12 @@ const Tooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
React.useImperativeHandle(ref, () => ({
forceAlign,
forcePopupAlign: () => {
warning(false, 'Tooltip', '`forcePopupAlign` is align to `forceAlign` instead.');
warning(
false,
'Tooltip',
'deprecated',
'`forcePopupAlign` is align to `forceAlign` instead.',
);
forceAlign();
},
}));
@ -242,6 +249,7 @@ const Tooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
warning(
!(deprecatedName in props),
'Tooltip',
'deprecated',
`\`${deprecatedName}\` is deprecated, please use \`${newName}\` instead.`,
);
});
@ -249,12 +257,14 @@ const Tooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
warning(
!destroyTooltipOnHide || typeof destroyTooltipOnHide === 'boolean',
'Tooltip',
'usage',
'`destroyTooltipOnHide` no need config `keepParent` anymore. Please use `boolean` value directly.',
);
warning(
!arrow || typeof arrow === 'boolean' || !('arrowPointAtCenter' in arrow),
'Tooltip',
'deprecated',
'`arrowPointAtCenter` in `arrow` is deprecated, please use `pointAtCenter` instead.',
);
}

View File

@ -1,10 +1,11 @@
import classNames from 'classnames';
import type { ChangeEvent, CSSProperties } from 'react';
import React, { useCallback, useContext } from 'react';
import classNames from 'classnames';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import { groupDisabledKeysMap, groupKeysMap } from '../_util/transKeys';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
@ -169,9 +170,12 @@ const Transfer = <RecordType extends TransferItem = TransferItem>(
] = useSelection(leftDataSource, rightDataSource, selectedKeys);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!pagination || !children,
'Transfer',
'usage',
'`pagination` not support customize render list.',
);
}

View File

@ -1,3 +1,4 @@
import * as React from 'react';
import classNames from 'classnames';
import type { BaseSelectRef } from 'rc-select';
import type { Placement } from 'rc-select/lib/BaseSelect';
@ -5,23 +6,23 @@ import type { TreeSelectProps as RcTreeSelectProps } from 'rc-tree-select';
import RcTreeSelect, { SHOW_ALL, SHOW_CHILD, SHOW_PARENT, TreeNode } from 'rc-tree-select';
import type { BaseOptionType, DefaultOptionType } from 'rc-tree-select/lib/TreeSelect';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import genPurePanel from '../_util/PurePanel';
import type { SelectCommonPlacement } from '../_util/motion';
import { getTransitionName } from '../_util/motion';
import genPurePanel from '../_util/PurePanel';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import type { SizeType } from '../config-provider/SizeContext';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import DisabledContext from '../config-provider/DisabledContext';
import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context';
import useSelectStyle from '../select/style';
import useBuiltinPlacements from '../select/useBuiltinPlacements';
import useShowArrow from '../select/useShowArrow';
import getIcons from '../select/utils/iconUtil';
import useIcons from '../select/useIcons';
import { useCompactItemContext } from '../space/Compact';
import type { AntTreeNodeProps, TreeProps } from '../tree';
import type { SwitcherIcon } from '../tree/Tree';
@ -120,27 +121,33 @@ const InternalTreeSelect = <
} = React.useContext(ConfigContext);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
multiple !== false || !treeCheckable,
'TreeSelect',
'usage',
'`multiple` will always be `true` when `treeCheckable` is true',
);
warning(
!dropdownClassName,
'TreeSelect',
'deprecated',
'`dropdownClassName` is deprecated. Please use `popupClassName` instead.',
);
warning(
dropdownMatchSelectWidth === undefined,
'Select',
'deprecated',
'`dropdownMatchSelectWidth` is deprecated. Please use `popupMatchSelectWidth` instead.',
);
warning(
!('showArrow' in props),
'TreeSelect',
'deprecated',
'`showArrow` is deprecated which will be removed in next major version. It will be a default behavior, you can hide it by setting `suffixIcon` to null.',
);
}
@ -180,7 +187,7 @@ const InternalTreeSelect = <
const mergedStatus = getMergedStatus(contextStatus, customStatus);
// ===================== Icons =====================
const { suffixIcon, removeIcon, clearIcon } = getIcons({
const { suffixIcon, removeIcon, clearIcon } = useIcons({
...props,
multiple: isMultiple,
showSuffixIcon,

View File

@ -1,5 +1,6 @@
import * as React from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import type { BlockProps } from './Base';
import Base from './Base';
@ -10,11 +11,16 @@ export interface LinkProps
}
const Link = React.forwardRef<HTMLElement, LinkProps>(({ ellipsis, rel, ...restProps }, ref) => {
warning(
typeof ellipsis !== 'object',
'Typography.Link',
'`ellipsis` only supports boolean value.',
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
typeof ellipsis !== 'object',
'Typography.Link',
'usage',
'`ellipsis` only supports boolean value.',
);
}
const mergedProps = {
...restProps,

View File

@ -1,6 +1,7 @@
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import warning from '../_util/warning';
import omit from 'rc-util/lib/omit';
import { devUseWarning } from '../_util/warning';
import type { BlockProps, EllipsisConfig } from './Base';
import Base from './Base';
@ -22,13 +23,18 @@ const Text: React.ForwardRefRenderFunction<HTMLSpanElement, TextProps> = (
return ellipsis;
}, [ellipsis]);
warning(
typeof ellipsis !== 'object' ||
!ellipsis ||
(!('expandable' in ellipsis) && !('rows' in ellipsis)),
'Typography.Text',
'`ellipsis` do not support `expandable` or `rows` props.',
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
typeof ellipsis !== 'object' ||
!ellipsis ||
(!('expandable' in ellipsis) && !('rows' in ellipsis)),
'Typography.Text',
'usage',
'`ellipsis` do not support `expandable` or `rows` props.',
);
}
return <Base ref={ref} {...restProps} ellipsis={mergedEllipsis} component="span" />;
};

View File

@ -1,5 +1,6 @@
import * as React from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import type { BlockProps } from './Base';
import Base from './Base';
@ -18,14 +19,20 @@ const Title = React.forwardRef<HTMLElement, TitleProps>((props, ref) => {
const { level = 1, ...restProps } = props;
let component: keyof JSX.IntrinsicElements;
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
TITLE_ELE_LIST.includes(level),
'Typography.Title',
'usage',
'Title only accept `1 | 2 | 3 | 4 | 5` as `level` value. And `5` need 4.6.0+ version.',
);
}
if (TITLE_ELE_LIST.includes(level)) {
component = `h${level}`;
} else {
warning(
false,
'Typography.Title',
'Title only accept `1 | 2 | 3 | 4 | 5` as `level` value. And `5` need 4.6.0+ version.',
);
component = 'h1';
}

View File

@ -1,7 +1,8 @@
import * as React from 'react';
import classNames from 'classnames';
import { composeRef } from 'rc-util/lib/ref';
import * as React from 'react';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import type { ConfigConsumerProps, DirectionType } from '../config-provider';
import { ConfigContext } from '../config-provider';
import useStyle from './style';
@ -51,10 +52,20 @@ const Typography = React.forwardRef<
let mergedRef = ref;
if (setContentRef) {
warning(false, 'Typography', '`setContentRef` is deprecated. Please use `ref` instead.');
mergedRef = composeRef(ref, setContentRef);
}
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!setContentRef,
'Typography',
'deprecated',
'`setContentRef` is deprecated. Please use `ref` instead.',
);
}
const prefixCls = getPrefixCls('typography', customizePrefixCls);
// Style

View File

@ -1,15 +1,15 @@
import * as React from 'react';
import { flushSync } from 'react-dom';
import classNames from 'classnames';
import type { UploadProps as RcUploadProps } from 'rc-upload';
import RcUpload from 'rc-upload';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import * as React from 'react';
import { flushSync } from 'react-dom';
import warning from '../_util/warning';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import { useLocale } from '../locale';
import defaultLocale from '../locale/en_US';
import UploadList from './UploadList';
import type {
RcFile,
ShowUploadListInterface,
@ -18,6 +18,7 @@ import type {
UploadProps,
} from './interface';
import useStyle from './style';
import UploadList from './UploadList';
import { file2Obj, getFileItem, removeFileItem, updateFileList } from './utils';
export const LIST_IGNORE = `__LIST_IGNORE_${Date.now()}__`;
@ -78,17 +79,23 @@ const InternalUpload: React.ForwardRefRenderFunction<UploadRef, UploadProps> = (
const upload = React.useRef<RcUpload>(null);
warning(
'fileList' in props || !('value' in props),
'Upload',
'`value` is not a valid prop, do you mean `fileList`?',
);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning();
warning(
!('transformFile' in props),
'Upload',
'`transformFile` is deprecated. Please use `beforeUpload` directly.',
);
warning(
'fileList' in props || !('value' in props),
'Upload',
'usage',
'`value` is not a valid prop, do you mean `fileList`?',
);
warning(
!('transformFile' in props),
'Upload',
'deprecated',
'`transformFile` is deprecated. Please use `beforeUpload` directly.',
);
}
// Control mode will auto fill file uid if not provided
React.useMemo(() => {