feat: ⌨️ Popconfirm can be closed by pressing ESC (#24420)

* feat: support ESC to close Popconfirm

close #15203

* add test case

* fix focus in other element inside inner-content

* no fit

* fix snapshot

* refactor it

* use util cloneElement

* fix test case
This commit is contained in:
偏右 2020-05-25 17:43:59 +08:00 committed by GitHub
parent 4b485d393b
commit c93400d375
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 4 deletions

View File

@ -176,4 +176,23 @@ describe('Popconfirm', () => {
triggerNode.simulate('click');
expect(ref.current.getPopupDomNode()).toBeFalsy();
});
it('should be closed by pressing ESC', () => {
const onVisibleChange = jest.fn();
const wrapper = mount(
<Popconfirm
title="title"
mouseEnterDelay={0}
mouseLeaveDelay={0}
onVisibleChange={onVisibleChange}
>
<span>Delete</span>
</Popconfirm>,
);
const triggerNode = wrapper.find('span').at(0);
triggerNode.simulate('click');
expect(onVisibleChange).toHaveBeenLastCalledWith(true, undefined);
triggerNode.simulate('keydown', { key: 'Escape', keyCode: 27 });
expect(onVisibleChange).toHaveBeenLastCalledWith(false, eventObject);
});
});

View File

@ -1,5 +1,6 @@
import * as React from 'react';
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import KeyCode from 'rc-util/lib/KeyCode';
import Tooltip, { AbstractTooltipProps } from '../tooltip';
import Button from '../button';
import { LegacyButtonType, NativeButtonProps, convertLegacyProps } from '../button/button';
@ -7,6 +8,7 @@ import LocaleReceiver from '../locale-provider/LocaleReceiver';
import defaultLocale from '../locale/default';
import { ConfigContext } from '../config-provider';
import { getRenderPropValue, RenderFunction } from '../_util/getRenderPropValue';
import { cloneElement } from '../_util/reactNode';
export interface PopconfirmProps extends AbstractTooltipProps {
title: React.ReactNode | RenderFunction;
@ -19,7 +21,10 @@ export interface PopconfirmProps extends AbstractTooltipProps {
okButtonProps?: NativeButtonProps;
cancelButtonProps?: NativeButtonProps;
icon?: React.ReactNode;
onVisibleChange?: (visible: boolean, e?: React.MouseEvent<HTMLElement>) => void;
onVisibleChange?: (
visible: boolean,
e?: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLDivElement>,
) => void;
}
export interface PopconfirmState {
@ -46,7 +51,10 @@ const Popconfirm = React.forwardRef<unknown, PopconfirmProps>((props, ref) => {
}
}, [props.defaultVisible]);
const settingVisible = (value: boolean, e?: React.MouseEvent<HTMLButtonElement>) => {
const settingVisible = (
value: boolean,
e?: React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLDivElement>,
) => {
if (!('visible' in props)) {
setVisible(value);
}
@ -72,6 +80,12 @@ const Popconfirm = React.forwardRef<unknown, PopconfirmProps>((props, ref) => {
}
};
const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.keyCode === KeyCode.ESC && visible) {
settingVisible(false, e);
}
};
const onVisibleChange = (value: boolean) => {
const { disabled } = props;
if (disabled) {
@ -107,7 +121,7 @@ const Popconfirm = React.forwardRef<unknown, PopconfirmProps>((props, ref) => {
const { getPrefixCls } = React.useContext(ConfigContext);
const { prefixCls: customizePrefixCls, placement, ...restProps } = props;
const { prefixCls: customizePrefixCls, placement, children, ...restProps } = props;
const prefixCls = getPrefixCls('popover', customizePrefixCls);
const overlay = (
@ -125,7 +139,14 @@ const Popconfirm = React.forwardRef<unknown, PopconfirmProps>((props, ref) => {
visible={visible}
overlay={overlay}
ref={ref as any}
/>
>
{cloneElement(children, {
onKeyDown: (e: React.KeyboardEvent<any>) => {
children?.props.onKeyDown?.(e);
onKeyDown(e);
},
})}
</Tooltip>
);
});