chore: support deprecated visible in v5 but warning with this (#37422)

* chore: tags deprecated support

* chore: Slider Tooltip test

* chore: Table dropdown visible legacy

* chore: drawer warning of legacy

* chore: Modal legacy prop

* chore: dropdown open legacy prop

* chore: Tooltiop visible legacy info

* fix: format logic

* test: Update test for 18

* chore: fix lint
This commit is contained in:
二货爱吃白萝卜 2022-09-06 21:46:49 +08:00 committed by GitHub
parent f85c68f4be
commit 44d8076304
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 382 additions and 147 deletions

View File

@ -106,7 +106,7 @@ describe('Drawer', () => {
expect(container.querySelector('.ant-drawer-content-wrapper-hidden')).toBeTruthy();
});
it('test afterVisibleChange', async () => {
it('test afterOpenChange', async () => {
const afterOpenChange = jest.fn();
const { container, rerender } = render(<DrawerTest open afterOpenChange={afterOpenChange} />);
rerender(<DrawerTest open={false} afterOpenChange={afterOpenChange} />);
@ -119,6 +119,31 @@ describe('Drawer', () => {
expect(afterOpenChange).toHaveBeenCalledTimes(1);
});
it('test legacy afterVisibleChange', async () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const afterVisibleChange = jest.fn();
const { container, rerender } = render(
<DrawerTest open afterVisibleChange={afterVisibleChange} />,
);
rerender(<DrawerTest visible={false} afterVisibleChange={afterVisibleChange} />);
act(() => {
jest.runAllTimers();
});
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper')!);
expect(afterVisibleChange).toHaveBeenCalledTimes(1);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Drawer] `visible` is deprecated, please use `open` instead.',
);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Drawer] `afterVisibleChange` is deprecated, please use `afterOpenChange` instead.',
);
errorSpy.mockRestore();
});
it('should support children ref', () => {
const fn = jest.fn();

View File

@ -39,6 +39,12 @@ export interface DrawerProps extends RcDrawerProps {
extra?: React.ReactNode;
afterOpenChange?: (open: boolean) => void;
// Deprecated
/** @deprecated Please use `open` instead */
visible?: boolean;
/** @deprecated Please use `afterOpenChange` instead */
afterVisibleChange?: (open: boolean) => void;
}
const defaultPushState: PushState = { distance: 180 };
@ -56,6 +62,7 @@ function Drawer(props: DrawerProps) {
bodyStyle,
drawerStyle,
open,
afterOpenChange,
children,
title,
headerStyle,
@ -65,6 +72,11 @@ function Drawer(props: DrawerProps) {
prefixCls: customizePrefixCls,
getContainer: customizeGetContainer,
extra,
// Deprecated
visible,
afterVisibleChange,
...rest
} = props;
@ -86,17 +98,6 @@ function Drawer(props: DrawerProps) {
</button>
);
[
['visible', 'open'],
['afterVisibleChange', 'afterOpenChange'],
].forEach(([deprecatedName, newName]) => {
warning(
!(deprecatedName in props),
'Drawer',
`\`${deprecatedName}\` is deprecated which will be removed in next major version, please use \`${newName}\` instead.`,
);
});
function renderHeader() {
if (!title && !closable) {
return null;
@ -149,7 +150,7 @@ function Drawer(props: DrawerProps) {
warning(
!(deprecatedName in props),
'Drawer',
`\`${deprecatedName}\` is removed, please use \`${newName}\` instead.`,
`\`${deprecatedName}\` is deprecated, please use \`${newName}\` instead.`,
);
});
}
@ -187,13 +188,14 @@ function Drawer(props: DrawerProps) {
maskMotion={maskMotion}
motion={panelMotion}
{...rest}
open={open}
open={open ?? visible}
mask={mask}
push={push}
width={mergedWidth}
height={mergedHeight}
rootClassName={drawerClassName}
getContainer={getContainer}
afterOpenChange={afterOpenChange ?? afterVisibleChange}
>
<div className={`${prefixCls}-wrapper-body`} style={{ ...drawerStyle }}>
{renderHeader()}

View File

@ -6,6 +6,7 @@ import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { act, fireEvent, render, sleep } from '../../../tests/utils';
import Menu from '../../menu';
import { resetWarned } from '../../_util/warning';
let triggerProps: TriggerProps;
@ -167,4 +168,37 @@ describe('Dropdown', () => {
jest.useRealTimers();
});
it('legacy visible', () => {
resetWarned();
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const onOpenChange = jest.fn();
const onVisibleChange = jest.fn();
const { container } = render(
<Dropdown
visible
onOpenChange={onOpenChange}
onVisibleChange={onVisibleChange}
trigger={['click']}
overlay={<div className="bamboo" />}
>
<a className="little" />
</Dropdown>,
);
expect(document.querySelector('.bamboo')).toBeTruthy();
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Dropdown] `visible` is deprecated, please use `open` instead.',
);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Dropdown] `onVisibleChange` is deprecated, please use `onOpenChange` instead.',
);
fireEvent.click(container.querySelector('.little')!);
expect(onOpenChange).toHaveBeenCalled();
expect(onVisibleChange).toHaveBeenCalled();
errorSpy.mockRestore();
});
});

View File

@ -68,6 +68,12 @@ export interface DropdownProps {
mouseLeaveDelay?: number;
openClassName?: string;
children?: React.ReactNode;
// Deprecated
/** @deprecated Please use `open` instead */
visible?: boolean;
/** @deprecated Please use `onOpenChange` instead */
onVisibleChange?: (open: boolean) => void;
}
interface DropdownInterface extends React.FC<DropdownProps> {
@ -82,17 +88,6 @@ const Dropdown: DropdownInterface = props => {
direction,
} = React.useContext(ConfigContext);
[
['visible', 'open'],
['onVisibleChange', 'onOpenChange'],
].forEach(([deprecatedName, newName]) => {
warning(
!(deprecatedName in props),
'Dropdown',
`\`${deprecatedName}\` is deprecated, please use \`${newName}\` instead.`,
);
});
const getTransitionName = () => {
const rootPrefixCls = getPrefixCls();
const { placement = '', transitionName } = props;
@ -134,8 +129,25 @@ const Dropdown: DropdownInterface = props => {
overlayClassName,
open,
onOpenChange,
// Deprecated
visible,
onVisibleChange,
} = props;
if (process.env.NODE_ENV !== 'production') {
[
['visible', 'open'],
['onVisibleChange', 'onOpenChange'],
].forEach(([deprecatedName, newName]) => {
warning(
!(deprecatedName in props),
'Dropdown',
`\`${deprecatedName}\` is deprecated, please use \`${newName}\` instead.`,
);
});
}
const prefixCls = getPrefixCls('dropdown', customizePrefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls);
@ -160,11 +172,12 @@ const Dropdown: DropdownInterface = props => {
// =========================== Open ============================
const [mergedOpen, setOpen] = useMergedState(false, {
value: open,
value: open ?? visible,
});
const onInnerOpenChange = useEvent((nextOpen: boolean) => {
onOpenChange?.(nextOpen);
onVisibleChange?.(nextOpen);
setOpen(nextOpen);
});

View File

@ -82,6 +82,10 @@ export interface ModalProps {
modalRender?: (node: React.ReactNode) => React.ReactNode;
focusTriggerAfterClose?: boolean;
children?: React.ReactNode;
// Legacy
/** @deprecated Please use `open` instead. */
visible?: boolean;
}
type getContainerFunc = () => HTMLElement;
@ -148,14 +152,6 @@ const Modal: React.FC<ModalProps> = props => {
onOk?.(e);
};
if (process.env.NODE_ENV !== 'production') {
warning(
!('visible' in props),
'Modal',
`\`visible\` is removed in v5, please use \`open\` instead.`,
);
}
const {
prefixCls: customizePrefixCls,
className,
@ -165,6 +161,10 @@ const Modal: React.FC<ModalProps> = props => {
getContainer,
closeIcon,
focusTriggerAfterClose = true,
// Deprecated
visible,
...restProps
} = props;
@ -178,11 +178,9 @@ const Modal: React.FC<ModalProps> = props => {
[`${prefixCls}-wrap-rtl`]: direction === 'rtl',
});
warning(
!('visible' in props),
'Modal',
`\`visible\` is deprecated, please use \`open\` instead.`,
);
if (process.env.NODE_ENV !== 'production') {
warning(!('visible' in props), 'Modal', '`visible` is deprecated, please use `open` instead.');
}
return wrapSSR(
<NoFormStyle status override>
@ -199,7 +197,7 @@ const Modal: React.FC<ModalProps> = props => {
onOk: handleOk,
onCancel: handleCancel,
})}
visible={open}
visible={open ?? visible}
mousePosition={mousePosition}
onClose={handleCancel}
closeIcon={renderCloseIcon(prefixCls, closeIcon)}
@ -215,7 +213,6 @@ const Modal: React.FC<ModalProps> = props => {
Modal.defaultProps = {
width: 520,
confirmLoading: false,
open: false,
};
export default Modal;

View File

@ -96,11 +96,13 @@ describe('Modal', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<Modal {...({ visible: true } as any)} />);
render(<Modal visible />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Modal] `visible` is removed in v5, please use `open` instead.',
'Warning: [antd: Modal] `visible` is deprecated, please use `open` instead.',
);
expect(document.querySelector('.ant-modal')).toBeTruthy();
errSpy.mockRestore();
});
});

View File

@ -168,27 +168,58 @@ describe('Slider', () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const { rerender } = render(<TSSlider tooltipPrefixCls="xxx" />);
const { container, rerender } = render(<TSSlider tooltipPrefixCls="xxx" />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Slider] `tooltipPrefixCls` is removed in v5, please use `tooltip.prefixCls` instead.',
'Warning: [antd: Slider] `tooltipPrefixCls` is deprecated, please use `tooltip.prefixCls` instead.',
);
rerender(<TSSlider getTooltipPopupContainer={() => document.body} />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Slider] `getTooltipPopupContainer` is removed in v5, please use `tooltip.getPopupContainer` instead.',
'Warning: [antd: Slider] `getTooltipPopupContainer` is deprecated, please use `tooltip.getPopupContainer` instead.',
);
rerender(<TSSlider tipFormatter={(v: any) => v} />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Slider] `tipFormatter` is removed in v5, please use `tooltip.formatter` instead.',
'Warning: [antd: Slider] `tipFormatter` is deprecated, please use `tooltip.formatter` instead.',
);
rerender(<TSSlider tooltipVisible />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Slider] `tooltipVisible` is removed in v5, please use `tooltip.open` instead.',
'Warning: [antd: Slider] `tooltipVisible` is deprecated, please use `tooltip.open` instead.',
);
rerender(<TSSlider tooltipPlacement="left" />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Slider] `tooltipPlacement` is removed in v5, please use `tooltip.placement` instead.',
'Warning: [antd: Slider] `tooltipPlacement` is deprecated, please use `tooltip.placement` instead.',
);
// All should work
const holder = document.createElement('div');
holder.id = 'holder';
document.body.appendChild(holder);
const getTooltipPopupContainer = jest.fn(() => container);
rerender(
<TSSlider
tooltipPrefixCls="bamboo"
getTooltipPopupContainer={getTooltipPopupContainer}
tipFormatter={() => 'little'}
tooltipPlacement="bottom"
tooltipVisible
/>,
);
act(() => {
jest.runAllTimers();
});
expect(getTooltipPopupContainer).toHaveBeenCalled();
expect(container.querySelector('.bamboo')).toBeTruthy();
expect(container.querySelector('.bamboo-inner')!.textContent).toEqual('little');
holder.parentNode?.removeChild(holder);
errSpy.mockRestore();
});
});

View File

@ -23,12 +23,15 @@ export type HandleGeneratorFn = (config: {
info: HandleGeneratorInfo;
}) => React.ReactElement;
export type Formatter = (value?: number) => React.ReactNode;
const defaultFormatter: Formatter = val => (typeof val === 'number' ? val.toString() : '');
export interface SliderTooltipProps {
prefixCls?: string;
open?: boolean;
placement?: TooltipPlacement;
getPopupContainer?: (triggerNode: HTMLElement) => HTMLElement;
formatter?: null | ((value?: number) => React.ReactNode);
formatter?: null | Formatter;
}
export interface SliderBaseProps {
@ -47,6 +50,21 @@ export interface SliderBaseProps {
style?: React.CSSProperties;
tooltip?: SliderTooltipProps;
autoFocus?: boolean;
// Deprecated
/** @deprecated `tooltipPrefixCls` is deprecated. Please use `tooltip.prefixCls` instead. */
tooltipPrefixCls?: string;
/** @deprecated `tipFormatter` is deprecated. Please use `tooltip.formatter` instead. */
tipFormatter?: null | ((value?: number) => React.ReactNode);
/** @deprecated `tooltipVisible` is deprecated. Please use `tooltip.open` instead. */
tooltipVisible?: boolean;
/**
* @deprecated `getTooltipPopupContainer` is deprecated. Please use `tooltip.getPopupContainer`
* instead.
*/
getTooltipPopupContainer?: (triggerNode: HTMLElement) => HTMLElement;
/** @deprecated `tooltipPlacement` is deprecated. Please use `tooltip.placement` instead. */
tooltipPlacement?: TooltipPlacement;
}
export interface SliderSingleProps extends SliderBaseProps {
@ -77,6 +95,21 @@ export type Opens = { [index: number]: boolean };
const Slider = React.forwardRef<unknown, SliderSingleProps | SliderRangeProps>(
(props, ref: any) => {
const {
prefixCls: customizePrefixCls,
range,
className,
// Deprecated Props
tooltipPrefixCls: legacyTooltipPrefixCls,
tipFormatter: legacyTipFormatter,
tooltipVisible: legacyTooltipVisible,
getTooltipPopupContainer: legacyGetTooltipPopupContainer,
tooltipPlacement: legacyTooltipPlacement,
...restProps
} = props;
const { getPrefixCls, direction, getPopupContainer } = React.useContext(ConfigContext);
const [opens, setOpens] = React.useState<Opens>({});
@ -84,9 +117,9 @@ const Slider = React.forwardRef<unknown, SliderSingleProps | SliderRangeProps>(
setOpens((prev: Opens) => ({ ...prev, [index]: open }));
};
const getTooltipPlacement = (tooltipPlacement?: TooltipPlacement, vertical?: boolean) => {
if (tooltipPlacement) {
return tooltipPlacement;
const getTooltipPlacement = (placement?: TooltipPlacement, vertical?: boolean) => {
if (placement) {
return placement;
}
if (!vertical) {
return 'top';
@ -94,7 +127,6 @@ const Slider = React.forwardRef<unknown, SliderSingleProps | SliderRangeProps>(
return direction === 'rtl' ? 'left' : 'right';
};
const { prefixCls: customizePrefixCls, range, className, ...restProps } = props;
const prefixCls = getPrefixCls('slider', customizePrefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls);
@ -133,7 +165,7 @@ const Slider = React.forwardRef<unknown, SliderSingleProps | SliderRangeProps>(
warning(
!(deprecatedName in props),
'Slider',
`\`${deprecatedName}\` is removed in v5, please use \`tooltip.${newName}\` instead.`,
`\`${deprecatedName}\` is deprecated, please use \`tooltip.${newName}\` instead.`,
);
});
}
@ -145,9 +177,6 @@ const Slider = React.forwardRef<unknown, SliderSingleProps | SliderRangeProps>(
const { tooltip = {}, vertical } = props;
const tooltipProps: SliderTooltipProps = {
formatter(value) {
return typeof value === 'number' ? value.toString() : '';
},
...tooltip,
};
const {
@ -158,8 +187,18 @@ const Slider = React.forwardRef<unknown, SliderSingleProps | SliderRangeProps>(
formatter: tipFormatter,
} = tooltipProps;
const isTipFormatter = tipFormatter ? opens[index] || dragging : false;
const open = tooltipOpen || (tooltipOpen === undefined && isTipFormatter);
let mergedTipFormatter = tipFormatter;
if (tipFormatter || tipFormatter === null) {
mergedTipFormatter = tipFormatter;
} else if (legacyTipFormatter || legacyTipFormatter === null) {
mergedTipFormatter = legacyTipFormatter;
} else {
mergedTipFormatter = defaultFormatter;
}
const isTipFormatter = mergedTipFormatter ? opens[index] || dragging : false;
const open =
tooltipOpen ?? legacyTooltipVisible ?? (tooltipOpen === undefined && isTipFormatter);
const passedProps = {
...node.props,
@ -167,18 +206,23 @@ const Slider = React.forwardRef<unknown, SliderSingleProps | SliderRangeProps>(
onMouseLeave: () => toggleTooltipOpen(index, false),
};
const tooltipPrefixCls = getPrefixCls('tooltip', customizeTooltipPrefixCls);
const tooltipPrefixCls = getPrefixCls(
'tooltip',
customizeTooltipPrefixCls ?? legacyTooltipPrefixCls,
);
return (
<SliderTooltip
prefixCls={tooltipPrefixCls}
title={tipFormatter ? tipFormatter(info.value) : ''}
title={mergedTipFormatter ? mergedTipFormatter(info.value) : ''}
open={open}
placement={getTooltipPlacement(tooltipPlacement, vertical)}
placement={getTooltipPlacement(tooltipPlacement ?? legacyTooltipPlacement, vertical)}
transitionName={`${rootPrefixCls}-zoom-down`}
key={index}
overlayClassName={`${prefixCls}-tooltip`}
getPopupContainer={getTooltipPopupContainer || getPopupContainer}
getPopupContainer={
getTooltipPopupContainer || legacyGetTooltipPopupContainer || getPopupContainer
}
>
{React.cloneElement(node, passedProps)}
</SliderTooltip>

View File

@ -140,28 +140,13 @@ function InternalTable<RecordType extends object = any>(
showSorterTooltip = true,
} = props;
warning(
!(typeof rowKey === 'function' && rowKey.length > 1),
'Table',
'`index` parameter of `rowKey` function is deprecated. There is no guarantee that it will work as expected.',
);
warning(
!('filterDropdownVisible' in props || 'onFilterDropdownVisibleChange' in props),
'Table',
'`filterDropdownVisible` and `onFilterDropdownVisibleChange` is deprecated, ' +
'please use `filterDropdownOpen` and `onFilterDropdownOpenChange` instead.',
);
[
['filterDropdownVisible', 'filterDropdownOpen'],
['onFilterDropdownVisibleChange', 'onFilterDropdownOpenChange'],
].forEach(([deprecatedName, newName]) => {
if (process.env.NODE_ENV !== 'production') {
warning(
!(deprecatedName in props),
!(typeof rowKey === 'function' && rowKey.length > 1),
'Table',
`\`${deprecatedName}\` is deprecated which will be removed in next major version.Please use \`${newName}\` instead. `,
'`index` parameter of `rowKey` function is deprecated. There is no guarantee that it will work as expected.',
);
});
}
const baseColumns = React.useMemo(
() => columns || (convertChildrenToColumns(children) as ColumnsType<RecordType>),
@ -277,7 +262,8 @@ function InternalTable<RecordType extends object = any>(
/**
* Controlled state in `columns` is not a good idea that makes too many code (1000+ line?) to read
* state out and then put it back to title render. Move these code into `hooks` but still too
* complex. We should provides Table props like `sorter` & `filter` to handle control in next big version.
* complex. We should provides Table props like `sorter` & `filter` to handle control in next big
* version.
*/
// ============================ Sorter =============================

View File

@ -12,6 +12,7 @@ import Tooltip from '../../tooltip';
import type { SelectProps } from '../../select';
import type { ColumnGroupType, ColumnType, TableProps } from '..';
import type { ColumnFilterItem, FilterDropdownProps, FilterValue } from '../interface';
import { resetWarned } from '../../_util/warning';
// https://github.com/Semantic-Org/Semantic-UI-React/blob/72c45080e4f20b531fda2e3e430e384083d6766b/test/specs/modules/Dropdown/Dropdown-test.js#L73
const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => {} } };
@ -281,6 +282,7 @@ describe('Table.filter', () => {
}
test('filterDropdownOpen');
test('filterDropdownVisible');
});
it('if the filter is visible it should ignore the selectedKeys changes', () => {
@ -333,19 +335,31 @@ describe('Table.filter', () => {
});
it('fires change event when visible change', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const onFilterDropdownOpenChange = jest.fn();
const onFilterDropdownVisibleChange = jest.fn();
const { container } = render(
createTable({
columns: [
{
...column,
onFilterDropdownOpenChange,
onFilterDropdownVisibleChange,
},
],
}),
);
fireEvent.click(container.querySelector('.ant-dropdown-trigger')!);
expect(onFilterDropdownOpenChange).toHaveBeenCalledWith(true);
expect(onFilterDropdownVisibleChange).toHaveBeenCalledWith(true);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Table] `onFilterDropdownVisibleChange` is deprecated. Please use `onFilterDropdownOpenChange` instead.',
);
errSpy.mockRestore();
});
it('can be controlled by filteredValue', () => {

View File

@ -28,6 +28,7 @@ import type {
} from '../../interface';
import FilterSearch from './FilterSearch';
import FilterDropdownMenuWrapper from './FilterWrapper';
import warning from '../../../_util/warning';
type FilterTreeDataNode = FieldDataNode<{ title: React.ReactNode; key: React.Key }>;
@ -141,6 +142,10 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
onFilterDropdownOpenChange,
filterResetToDefaultFilteredValue,
defaultFilteredValue,
// Deprecated
filterDropdownVisible,
onFilterDropdownVisibleChange,
} = column;
const [visible, setVisible] = React.useState(false);
@ -151,9 +156,27 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
const triggerVisible = (newVisible: boolean) => {
setVisible(newVisible);
onFilterDropdownOpenChange?.(newVisible);
onFilterDropdownVisibleChange?.(newVisible);
};
const mergedVisible = typeof filterDropdownOpen === 'boolean' ? filterDropdownOpen : visible;
if (process.env.NODE_ENV !== 'production') {
[
['filterDropdownVisible', 'filterDropdownOpen', filterDropdownVisible],
[
'onFilterDropdownVisibleChange',
'onFilterDropdownOpenChange',
onFilterDropdownVisibleChange,
],
].forEach(([deprecatedName, newName, prop]) => {
warning(
prop === undefined || prop === null,
'Table',
`\`${deprecatedName}\` is deprecated. Please use \`${newName}\` instead.`,
);
});
}
const mergedVisible = filterDropdownOpen ?? filterDropdownVisible ?? visible;
// ===================== Select Keys =====================
const propFilteredKeys = filterState?.filteredKeys;

View File

@ -120,6 +120,12 @@ export interface ColumnType<RecordType> extends Omit<RcColumnType<RecordType>, '
// Responsive
responsive?: Breakpoint[];
// Deprecated
/** @deprecated Please use `filterDropdownOpen` instead */
filterDropdownVisible?: boolean;
/** @deprecated Please use `onFilterDropdownOpenChange` instead */
onFilterDropdownVisibleChange?: (visible: boolean) => void;
}
export interface ColumnGroupType<RecordType> extends Omit<ColumnType<RecordType>, 'dataIndex'> {

View File

@ -86,10 +86,11 @@ describe('Tag', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<Tag visible />);
const { container } = render(<Tag visible={false} />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Tag] `visible` is removed, please use `visible && <Tag />` instead.',
'Warning: [antd: Tag] `visible` is deprecated, please use `visible && <Tag />` instead.',
);
expect(container.querySelector('.ant-tag-hidden')).toBeTruthy();
errSpy.mockRestore();
});

View File

@ -20,6 +20,8 @@ export interface TagProps extends React.HTMLAttributes<HTMLSpanElement> {
color?: LiteralUnion<PresetColorType | PresetStatusColorType, string>;
closable?: boolean;
closeIcon?: React.ReactNode;
/** @deprecated `visible` will be removed in next major version. */
visible?: boolean;
onClose?: (e: React.MouseEvent<HTMLElement>) => void;
style?: React.CSSProperties;
icon?: React.ReactNode;
@ -51,6 +53,21 @@ const InternalTag: React.ForwardRefRenderFunction<HTMLSpanElement, TagProps> = (
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const [visible, setVisible] = React.useState(true);
// Warning for deprecated usage
if (process.env.NODE_ENV !== 'production') {
warning(
!('visible' in props),
'Tag',
'`visible` is deprecated, please use `visible && <Tag />` instead.',
);
}
React.useEffect(() => {
if ('visible' in props) {
setVisible(props.visible!);
}
}, [props.visible]);
const isPresetColor = (): boolean => {
if (!color) {
return false;
@ -103,12 +120,6 @@ const InternalTag: React.ForwardRefRenderFunction<HTMLSpanElement, TagProps> = (
return null;
};
warning(
!('visible' in props),
'Tag',
'`visible` is removed, please use `visible && <Tag />` instead.',
);
const isNeedWave =
'onClick' in props || (children && (children as React.ReactElement<any>).type === 'a');
const iconNode = icon || null;

View File

@ -4,13 +4,14 @@ import type { TooltipPlacement } from '..';
import Tooltip from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render, sleep, waitFor } from '../../../tests/utils';
import { fireEvent, render, sleep, waitFor, act } from '../../../tests/utils';
import Button from '../../button';
import DatePicker from '../../date-picker';
import Input from '../../input';
import Group from '../../input/Group';
import Switch from '../../switch';
import Radio from '../../radio';
import { resetWarned } from '../../_util/warning';
describe('Tooltip', () => {
mountTest(Tooltip);
@ -451,34 +452,61 @@ describe('Tooltip', () => {
});
it('deprecated warning', () => {
resetWarned();
jest.useFakeTimers();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const { rerender } = render(
<Tooltip visible>
// defaultVisible
const { container, rerender } = render(
<Tooltip defaultVisible title="bamboo">
<a />
</Tooltip>,
);
act(() => {
jest.runAllTimers();
});
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Tooltip] `defaultVisible` is deprecated, please use `defaultOpen` instead.',
);
expect(document.querySelector('.ant-tooltip')).toBeTruthy();
// visible
rerender(
<Tooltip visible title="bamboo">
<a />
</Tooltip>,
);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Tooltip] `visible` is deprecated, please use `open` instead.',
);
rerender(
<Tooltip defaultVisible>
<Tooltip visible={false} title="bamboo">
<a />
</Tooltip>,
);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Tooltip] `defaultVisible` is deprecated, please use `defaultOpen` instead.',
);
act(() => {
jest.runAllTimers();
});
if (container.querySelector('.ant-zoom-big-fast-leave-active')) {
fireEvent.animationEnd(container.querySelector('.ant-zoom-big-fast-leave-active')!);
}
expect(document.querySelector('.ant-tooltip-hidden')).toBeTruthy();
// onVisibleChange
rerender(
<Tooltip onVisibleChange={() => {}}>
<Tooltip onVisibleChange={() => {}} title="bamboo">
<a />
</Tooltip>,
);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Tooltip] `onVisibleChange` is deprecated, please use `onOpenChange` instead.',
);
// afterVisibleChange
rerender(
<Tooltip afterVisibleChange={() => {}}>
<Tooltip afterVisibleChange={() => {}} title="bamboo">
<a />
</Tooltip>,
);
@ -486,6 +514,28 @@ describe('Tooltip', () => {
'Warning: [antd: Tooltip] `afterVisibleChange` is deprecated, please use `afterOpenChange` instead.',
);
// Event Trigger
const onVisibleChange = jest.fn();
const afterVisibleChange = jest.fn();
rerender(
<Tooltip
visible
onVisibleChange={onVisibleChange}
afterVisibleChange={afterVisibleChange}
title="bamboo"
>
<a />
</Tooltip>,
);
fireEvent.mouseLeave(container.querySelector('a')!);
act(() => {
jest.runAllTimers();
});
expect(onVisibleChange).toHaveBeenCalled();
expect(afterVisibleChange).toHaveBeenCalled();
jest.useRealTimers();
errSpy.mockRestore();
});
});

View File

@ -51,30 +51,20 @@ interface LegacyTooltipProps
'children' | 'visible' | 'defaultVisible' | 'onVisibleChange' | 'afterVisibleChange'
>
> {
/**
* @deprecated `visible` is deprecated which will be removed in next major version. Please use
* `open` instead.
*/
visible?: RcTooltipProps['visible'];
open?: RcTooltipProps['visible'];
/**
* @deprecated `defaultVisible` is deprecated which will be removed in next major version. Please
* use `defaultOpen` instead.
*/
defaultVisible?: RcTooltipProps['defaultVisible'];
defaultOpen?: RcTooltipProps['defaultVisible'];
/**
* @deprecated `onVisibleChange` is deprecated which will be removed in next major version. Please
* use `onOpenChange` instead.
*/
onVisibleChange?: RcTooltipProps['onVisibleChange'];
onOpenChange?: RcTooltipProps['onVisibleChange'];
/**
* @deprecated `afterVisibleChange` is deprecated which will be removed in next major version.
* Please use `afterOpenChange` instead.
*/
afterVisibleChange?: RcTooltipProps['afterVisibleChange'];
afterOpenChange?: RcTooltipProps['afterVisibleChange'];
// Legacy
/** @deprecated Please use `open` instead. */
visible?: RcTooltipProps['visible'];
/** @deprecated Please use `defaultOpen` instead. */
defaultVisible?: RcTooltipProps['defaultVisible'];
/** @deprecated Please use `onOpenChange` instead. */
onVisibleChange?: RcTooltipProps['onVisibleChange'];
/** @deprecated Please use `afterOpenChange` instead. */
afterVisibleChange?: RcTooltipProps['afterVisibleChange'];
}
export interface AbstractTooltipProps extends LegacyTooltipProps {
@ -166,28 +156,42 @@ function getDisabledCompatibleChildren(element: React.ReactElement<any>, prefixC
}
const Tooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
openClassName,
getTooltipContainer,
overlayClassName,
color,
overlayInnerStyle,
children,
afterOpenChange,
afterVisibleChange,
} = props;
const {
getPopupContainer: getContextPopupContainer,
getPrefixCls,
direction,
} = React.useContext(ConfigContext);
[
['visible', 'open'],
['defaultVisible', 'defaultOpen'],
['onVisibleChange', 'onOpenChange'],
['afterVisibleChange', 'afterOpenChange'],
].forEach(([deprecatedName, newName]) => {
warning(
!(deprecatedName in props),
'Tooltip',
`\`${deprecatedName}\` is deprecated, please use \`${newName}\` instead.`,
);
});
if (process.env.NODE_ENV !== 'production') {
[
['visible', 'open'],
['defaultVisible', 'defaultOpen'],
['onVisibleChange', 'onOpenChange'],
['afterVisibleChange', 'afterOpenChange'],
].forEach(([deprecatedName, newName]) => {
warning(
!(deprecatedName in props),
'Tooltip',
`\`${deprecatedName}\` is deprecated, please use \`${newName}\` instead.`,
);
});
}
const [open, setOpen] = useMergedState(false, {
value: props.open,
defaultValue: props.defaultOpen,
value: props.open ?? props.visible,
defaultValue: props.defaultOpen ?? props.defaultVisible,
});
const isNoTitle = () => {
@ -256,15 +260,6 @@ const Tooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
const { getPopupContainer, overlayStyle, ...otherProps } = props;
const {
prefixCls: customizePrefixCls,
openClassName,
getTooltipContainer,
overlayClassName,
color,
overlayInnerStyle,
children,
} = props;
const prefixCls = getPrefixCls('tooltip', customizePrefixCls);
const rootPrefixCls = getPrefixCls();
@ -272,7 +267,7 @@ const Tooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
let tempOpen = open;
// Hide tooltip when there is no title
if (!('open' in props) && isNoTitle()) {
if (!('open' in props) && !('visible' in props) && isNoTitle()) {
tempOpen = false;
}
@ -320,6 +315,7 @@ const Tooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
overlay={getOverlay()}
visible={tempOpen}
onVisibleChange={onOpenChange}
afterVisibleChange={afterOpenChange ?? afterVisibleChange}
onPopupAlign={onPopupAlign}
overlayInnerStyle={formattedOverlayInnerStyle}
arrowContent={<span className={`${prefixCls}-arrow-content`} />}

View File

@ -106,7 +106,7 @@
],
"dependencies": {
"@ant-design/colors": "^6.0.0",
"@ant-design/cssinjs": "^0.0.0-alpha.40",
"@ant-design/cssinjs": "^0.0.0-alpha.44",
"@ant-design/icons": "^4.7.0",
"@ant-design/react-slick": "~0.29.1",
"@babel/runtime": "^7.18.3",