Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Portal event bubble #204

Merged
merged 2 commits into from Oct 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion examples/ant-design.tsx
@@ -1,6 +1,8 @@
/* eslint no-console:0 */
import '../assets/index.less';
import * as React from 'react';
import Select from 'rc-select';
import 'rc-select/assets/index.less';
import Dialog from '../src/DialogWrap';

const clearPath =
Expand Down Expand Up @@ -133,7 +135,11 @@ const MyControl = () => {
<button type="button" onClick={toggleCloseIcon}>
use custom icon, is using icon: {(useIcon && 'true') || 'false'}.
</button>
<div style={{ height: 200 }} />
<div style={{ height: 200 }}>
<Select dropdownStyle={{ zIndex: 9999999 }}>
<Select.Option value="light">Light</Select.Option>
</Select>
</div>
</Dialog>
);

Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -69,6 +69,7 @@
"np": "^6.4.0",
"prettier": "^2.1.1",
"rc-drawer": "4.1.0",
"rc-select": "^11.4.1",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-draggable": "^4.4.3",
Expand Down
3 changes: 3 additions & 0 deletions src/Dialog/Content.tsx
Expand Up @@ -11,6 +11,7 @@ export interface ContentProps extends IDialogChildProps {
motionName: string;
ariaId: string;
onVisibleChanged: (visible: boolean) => void;
onClick: React.MouseEventHandler;
}

export interface ContentRef {
Expand Down Expand Up @@ -41,6 +42,7 @@ const Content = React.forwardRef<ContentRef, ContentProps>((props, ref) => {
ariaId,
onClose,
onVisibleChanged,
onClick,
mousePosition,
} = props;

Expand Down Expand Up @@ -142,6 +144,7 @@ const Content = React.forwardRef<ContentRef, ContentProps>((props, ref) => {
ref={motionRef}
style={{ ...motionStyle, ...style, ...contentStyle }}
className={classNames(prefixCls, className, motionClassName)}
onClick={onClick}
>
<div tabIndex={0} ref={sentinelStartRef} style={sentinelStyle} aria-hidden="true" />
{modalRender ? modalRender(content) : content}
Expand Down
21 changes: 20 additions & 1 deletion src/Dialog/index.tsx
Expand Up @@ -88,12 +88,29 @@ export default function Dialog(props: IDialogChildProps) {
onClose?.(e);
}

// >>> Content
const contentClickRef = useRef(false);
const contentTimeoutRef = useRef<number>();

// We need record content click incase content popup out of dialog
const onContentClick: React.MouseEventHandler = () => {
clearTimeout(contentTimeoutRef.current);
contentClickRef.current = true;

contentTimeoutRef.current = setTimeout(() => {
contentClickRef.current = false;
});
};

// >>> Wrapper
// Close only when element not on dialog
let onWrapperClick: (e: React.SyntheticEvent) => void = null;
if (maskClosable) {
onWrapperClick = (e) => {
if (!contains(contentRef.current.getDOM(), e.target as HTMLElement)) {
if (
!contentClickRef.current &&
!contains(contentRef.current.getDOM(), e.target as HTMLElement)
) {
onInternalClose(e);
}
};
Expand Down Expand Up @@ -126,6 +143,7 @@ export default function Dialog(props: IDialogChildProps) {
useEffect(
() => () => {
switchScrollingEffect();
clearTimeout(contentTimeoutRef.current);
},
[],
);
Expand Down Expand Up @@ -156,6 +174,7 @@ export default function Dialog(props: IDialogChildProps) {
>
<Content
{...props}
onClick={onContentClick}
ref={contentRef}
closable={closable}
ariaId={ariaIdRef.current}
Expand Down
39 changes: 39 additions & 0 deletions tests/portal.spec.tsx
@@ -0,0 +1,39 @@
/* eslint-disable react/no-render-return-value, max-classes-per-file, func-names, no-console */
import React from 'react';
import Select from 'rc-select';
import { act } from 'react-dom/test-utils';
import { mount } from 'enzyme';
import Dialog from '../src';

/**
* Since overflow scroll test need a clear env which may affect by other test.
* Use a clean env instead.
*/
describe('Dialog.Portal', () => {
beforeEach(() => {
jest.useFakeTimers();
});

afterEach(() => {
jest.useRealTimers();
});

it('event should bubble', () => {
const onClose = jest.fn();

const wrapper = mount(
<Dialog onClose={onClose} visible>
<Select virtual={false} open>
<Select.Option value="bamboo">Bamboo</Select.Option>
</Select>
</Dialog>,
);

act(() => {
jest.runAllTimers();
});

wrapper.find('.rc-select-item-option-content').simulate('click');
expect(onClose).not.toHaveBeenCalled();
});
});