Skip to content

Commit

Permalink
refactor: Dialog use rc-portal component instead (#294)
Browse files Browse the repository at this point in the history
* chore: install portal

* test: Fix test case

* chore: clean up
  • Loading branch information
zombieJ committed Sep 29, 2022
1 parent 2644f19 commit 4f70824
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 98 deletions.
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module.exports = {
setupFiles: ["./tests/setup.js"],
setupFilesAfterEnv: ["./tests/setupFilesAfterEnv.ts"],
snapshotSerializers: [require.resolve("enzyme-to-json/serializer")],
};
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,14 @@
},
"dependencies": {
"@babel/runtime": "^7.10.1",
"@rc-component/portal": "^1.0.0-8",
"classnames": "^2.2.6",
"rc-motion": "^2.3.0",
"rc-util": "^5.21.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.0.0",
"@types/enzyme": "^3.10.7",
"@types/jest": "^26.0.14",
"@types/react": "^16.9.2",
Expand Down
4 changes: 2 additions & 2 deletions src/Dialog/Content/Panel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useRef } from 'react';
import classNames from 'classnames';
import type { IDialogChildProps } from '..';
import MemoChildren from './MemoChildren';
import type { IDialogPropTypes } from '../../IDialogPropTypes';

const sentinelStyle = { width: 0, height: 0, overflow: 'hidden', outline: 'none' };

export interface PanelProps extends Omit<IDialogChildProps, 'getOpenCount'> {
export interface PanelProps extends Omit<IDialogPropTypes, 'getOpenCount'> {
prefixCls: string;
ariaId?: string;
onMouseDown?: React.MouseEventHandler;
Expand Down
26 changes: 9 additions & 17 deletions src/Dialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,20 @@ import KeyCode from 'rc-util/lib/KeyCode';
import useId from 'rc-util/lib/hooks/useId';
import contains from 'rc-util/lib/Dom/contains';
import pickAttrs from 'rc-util/lib/pickAttrs';
import type ScollLocker from 'rc-util/lib/Dom/scrollLocker';
import type { IDialogPropTypes } from '../IDialogPropTypes';
import Mask from './Mask';
import { getMotionName } from '../util';
import Content from './Content';
import type { ContentRef } from './Content/Panel';

export type IDialogChildProps = {
// zombieJ: This should be handle on top instead of each Dialog.
// TODO: refactor to remove this.
getOpenCount: () => number;
scrollLocker?: ScollLocker;
} & IDialogPropTypes;

export default function Dialog(props: IDialogChildProps) {
export default function Dialog(props: IDialogPropTypes) {
const {
prefixCls = 'rc-dialog',
zIndex,
visible = false,
keyboard = true,
focusTriggerAfterClose = true,
scrollLocker,
// scrollLocker,

// Wrapper
wrapStyle,
Expand Down Expand Up @@ -151,13 +143,13 @@ export default function Dialog(props: IDialogChildProps) {
[],
);

useEffect(() => {
if (animatedVisible) {
scrollLocker?.lock();
return scrollLocker?.unLock;
}
return () => {};
}, [animatedVisible, scrollLocker]);
// useEffect(() => {
// if (animatedVisible) {
// scrollLocker?.lock();
// return scrollLocker?.unLock;
// }
// return () => {};
// }, [animatedVisible, scrollLocker]);

// ========================= Render =========================
return (
Expand Down
47 changes: 24 additions & 23 deletions src/DialogWrap.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as React from 'react';
import Portal from 'rc-util/lib/PortalWrapper';
import type { IDialogChildProps } from './Dialog';
import Portal from '@rc-component/portal';
import Dialog from './Dialog';
import type { IDialogPropTypes } from './IDialogPropTypes';

Expand All @@ -23,34 +22,36 @@ const DialogWrap: React.FC<IDialogPropTypes> = (props: IDialogPropTypes) => {
}
}, [visible]);

// 渲染在当前 dom 里;
if (getContainer === false) {
return (
<Dialog
{...props}
getOpenCount={() => 2} // 不对 body 做任何操作。。
/>
);
}
// // 渲染在当前 dom 里;
// if (getContainer === false) {
// return (
// <Dialog
// {...props}
// getOpenCount={() => 2} // 不对 body 做任何操作。。
// />
// );
// }

// Destroy on close will remove wrapped div
if (!forceRender && destroyOnClose && !animatedVisible) {
return null;
}

return (
<Portal visible={visible} forceRender={forceRender} getContainer={getContainer}>
{(childProps: IDialogChildProps) => (
<Dialog
{...props}
destroyOnClose={destroyOnClose}
afterClose={() => {
afterClose?.();
setAnimatedVisible(false);
}}
{...childProps}
/>
)}
<Portal
open={visible || forceRender || animatedVisible}
autoDestroy={false}
getContainer={getContainer}
autoLock={visible || animatedVisible}
>
<Dialog
{...props}
destroyOnClose={destroyOnClose}
afterClose={() => {
afterClose?.();
setAnimatedVisible(false);
}}
/>
</Portal>
);
};
Expand Down
19 changes: 10 additions & 9 deletions tests/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/* eslint-disable react/no-render-return-value, max-classes-per-file, func-names, no-console */
import React, { cloneElement } from 'react';
import { act } from 'react-dom/test-utils';
import { render } from '@testing-library/react';
import type { ReactWrapper } from 'enzyme';
import { mount } from 'enzyme';
import Portal from 'rc-util/lib/Portal';
import KeyCode from 'rc-util/lib/KeyCode';
import type { DialogProps } from '../src';
import Dialog from '../src';
Expand Down Expand Up @@ -295,23 +295,24 @@ describe('dialog', () => {

describe('getContainer is false', () => {
it('not set', () => {
const wrapper = mount(
const { container } = render(
<Dialog visible>
<div>forceRender element</div>
<div className="bamboo" />
</Dialog>,
);
expect(wrapper.find('.rc-dialog-body > div').text()).toEqual('forceRender element');
expect(wrapper.find(Portal)).toHaveLength(1);

expect(container.querySelector('.bamboo')).toBeFalsy();
expect(document.body.querySelector('.bamboo')).toBeTruthy();
});

it('set to false', () => {
const wrapper = mount(
const { container } = render(
<Dialog visible getContainer={false}>
<div>forceRender element</div>
<div className="bamboo" />
</Dialog>,
);
expect(wrapper.find('.rc-dialog-body > div').text()).toEqual('forceRender element');
expect(wrapper.find(Portal)).toHaveLength(0);

expect(container.querySelector('.bamboo')).toBeTruthy();
});
});

Expand Down
84 changes: 37 additions & 47 deletions tests/scroll.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable react/no-render-return-value, max-classes-per-file, func-names, no-console */
import React from 'react';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import Dialog from '../src';

/**
Expand All @@ -17,75 +17,65 @@ describe('Dialog.Scroll', () => {
});

it('Single Dialog body overflow set correctly', () => {
const wrapper = mount(<Dialog />, { attachTo: document.body });
document.body.style.overflow = 'scroll';
const { unmount, rerender } = render(<Dialog visible />);

wrapper.setProps({ visible: true });
jest.runAllTimers();
wrapper.update();
expect(document.body.style.overflow).toBe('hidden');
expect(document.body).toHaveStyle({
overflowY: 'hidden',
});

wrapper.setProps({ visible: false });
jest.runAllTimers();
wrapper.update();
expect(document.body.style.overflow).toBe('scroll');
rerender(<Dialog />);
expect(document.body).not.toHaveStyle({
overflowY: 'hidden',
});

wrapper.unmount();
// wrapper.unmount();
unmount();
});

it('Multiple Dialog body overflow set correctly', () => {
document.body.style.overflow = 'scroll';

const Demo = ({ visible = false, visible2 = false, ...restProps }) => (
<div>
<Dialog {...restProps} visible={visible} />
<Dialog {...restProps} visible={visible2} />
</div>
);

const wrapper = mount(<Demo />, { attachTo: document.body });

expect(wrapper.find('.rc-dialog').length).toBe(0);

wrapper.setProps({ visible: true });
jest.runAllTimers();
const { rerender, unmount } = render(<Demo />);

expect(wrapper.find('div.rc-dialog').length).toBe(1);
expect(document.body.style.overflow).toBe('hidden');
expect(document.querySelector('.rc-dialog')).toBeFalsy();

wrapper.setProps({ visible2: true });
jest.runAllTimers();

expect(wrapper.find('div.rc-dialog').length).toBe(2);
expect(document.body.style.overflow).toBe('hidden');
rerender(<Demo visible />);
expect(document.querySelectorAll('.rc-dialog')).toHaveLength(1);
expect(document.body).toHaveStyle({
overflowY: 'hidden',
});

wrapper.setProps({
visible: false,
visible2: false,
rerender(<Demo visible visible2 />);
expect(document.querySelectorAll('.rc-dialog')).toHaveLength(2);
expect(document.body).toHaveStyle({
overflowY: 'hidden',
});
jest.runAllTimers();

expect(document.body.style.overflow).toBe('scroll');
rerender(<Demo />);
expect(document.body).not.toHaveStyle({
overflowY: 'hidden',
});

wrapper.setProps({
visible: true,
rerender(<Demo visible />);
expect(document.body).toHaveStyle({
overflowY: 'hidden',
});
jest.runAllTimers();
expect(document.body.style.overflow).toBe('hidden');

wrapper.setProps({
visible: false,
visible2: true,
rerender(<Demo visible2 />);
expect(document.body).toHaveStyle({
overflowY: 'hidden',
});
jest.runAllTimers();
expect(document.body.style.overflow).toBe('hidden');

wrapper.setProps({
visible: false,
visible2: false,
rerender(<Demo />);
expect(document.body).not.toHaveStyle({
overflowY: 'hidden',
});
jest.runAllTimers();
expect(document.body.style.overflow).toBe('scroll');
wrapper.unmount();

unmount();
});
});
1 change: 1 addition & 0 deletions tests/setupFilesAfterEnv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@testing-library/jest-dom';

1 comment on commit 4f70824

@vercel
Copy link

@vercel vercel bot commented on 4f70824 Sep 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.